python-docx:打开文件时出错-“文件头的魔数错误”/“EOFError”

Posted

技术标签:

【中文标题】python-docx:打开文件时出错-“文件头的魔数错误”/“EOFError”【英文标题】:python-docx: Error opening file - "Bad magic number for file header" / "EOFError" 【发布时间】:2021-01-28 22:54:49 【问题描述】:

我工作的公司分发使用 python-docx 库的文档汇编软件。该软件在每个生成的文档上运行一个函数,该函数打开文档并对未正确转义的字符(即“& amp;”->“&”)进行简单的搜索和替换。

仅供参考,实际的文档程序集使用 python-docx-template。但是,该错误发生在文档已经组装之后,并且该错误是由搜索和替换功能触发的,该功能仅使用python-docx。

最近,我们遇到了一些无法在客户端部署中生成文档的案例。他们在实例化文档对象的这一行上抛出错误:

doc = Document(docx=Path(doc_path))

我们发现了两个错误:

raise BadZipFile("Bad magic number for file header")

raise EOFError

该软件被广泛使用,我们以前从未遇到过这个问题。我们无法在我们的测试环境中重现它。该错误在过去一周才开始出现,但在更新后已经出现在几个客户身上。该软件将多次无法生成特定文档,但在尝试几次后会成功。

我们只看到一个文档发生这种情况,但所有文档都使用相同的搜索和替换功能,就像我说的那样,错误只是问题文档的间歇性。

此搜索和替换功能的代码没有变化,我想不出任何其他有意义的差异来解释我们的文档组装过程。

我在查找有关 python-docx 库可能导致此问题的信息时遇到了很多麻烦。这是否表明生成的文档已损坏?如果有人能够阐明可能的原因,那将非常有帮助!

这是两个错误的堆栈跟踪:

错误的幻数...

File "/home/user/app/application/document_assembly/core_da.py", line 524, in translate_ampersands
    doc = Document(docx=Path(doc_path))
  File "/home/user/app-venv/lib/python3.6/site-packages/docx/api.py", line 25, in Document
    document_part = Package.open(docx).main_document_part
  File "/home/user/app-venv/lib/python3.6/site-packages/docx/opc/package.py", line 116, in open
    pkg_reader = PackageReader.from_file(pkg_file)
  File "/home/user/app-venv/lib/python3.6/site-packages/docx/opc/pkgreader.py", line 36, in from_file
    phys_reader, pkg_srels, content_types
  File "/home/user/app-venv/lib/python3.6/site-packages/docx/opc/pkgreader.py", line 69, in _load_serialized_parts
    for partname, blob, reltype, srels in part_walker:
  File "/home/user/app-venv/lib/python3.6/site-packages/docx/opc/pkgreader.py", line 104, in _walk_phys_parts
    part_srels = PackageReader._srels_for(phys_reader, partname)
  File "/home/user/app-venv/lib/python3.6/site-packages/docx/opc/pkgreader.py", line 83, in _srels_for
    rels_xml = phys_reader.rels_xml_for(source_uri)
  File "/home/user/app-venv/lib/python3.6/site-packages/docx/opc/phys_pkg.py", line 129, in rels_xml_for
    rels_xml = self.blob_for(source_uri.rels_uri)
  File "/home/user/app-venv/lib/python3.6/site-packages/docx/opc/phys_pkg.py", line 108, in blob_for
    return self._zipf.read(pack_uri.membername)
  File "/usr/lib/python3.6/zipfile.py", line 1337, in read
    with self.open(name, "r", pwd) as fp:
  File "/usr/lib/python3.6/zipfile.py", line 1396, in open
    raise BadZipFile("Bad magic number for file header")

zipfile.BadZipFile: Bad magic number for file header

EOF错误

File "/home/user/app/application/document_assembly/core_da.py", line 524, in translate_ampersands
    doc = Document(docx=Path(doc_path))
  File "/home/user/app-venv/lib/python3.6/site-packages/docx/api.py", line 25, in Document
    document_part = Package.open(docx).main_document_part
  File "/home/user/app-venv/lib/python3.6/site-packages/docx/opc/package.py", line 116, in open
    pkg_reader = PackageReader.from_file(pkg_file)
  File "/home/user/app-venv/lib/python3.6/site-packages/docx/opc/pkgreader.py", line 36, in from_file
    phys_reader, pkg_srels, content_types
  File "/home/user/app-venv/lib/python3.6/site-packages/docx/opc/pkgreader.py", line 69, in _load_serialized_parts
    for partname, blob, reltype, srels in part_walker:
  File "/home/user/app-venv/lib/python3.6/site-packages/docx/opc/pkgreader.py", line 110, in _walk_phys_parts
    for partname, blob, reltype, srels in next_walker:
  File "/home/user/app-venv/lib/python3.6/site-packages/docx/opc/pkgreader.py", line 105, in _walk_phys_parts
    blob = phys_reader.blob_for(partname)
  File "/home/user/app-venv/lib/python3.6/site-packages/docx/opc/phys_pkg.py", line 108, in blob_for
    return self._zipf.read(pack_uri.membername)
  File "/usr/lib/python3.6/zipfile.py", line 1338, in read
    return fp.read()
  File "/usr/lib/python3.6/zipfile.py", line 858, in read
    buf += self._read1(self.MAX_N)
  File "/usr/lib/python3.6/zipfile.py", line 940, in _read1
    data += self._read2(n - len(data))
  File "/usr/lib/python3.6/zipfile.py", line 975, in _read2
    raise EOFError
EOFError

【问题讨论】:

这种情况随机发生表明您的搜索和替换代码可能将某些二进制文件(例如时间戳)误认为是值得替换的字符 &,而实际上不应该。跨度> 这发生在搜索和替换功能运行之前。当代码尝试在函数的最开始创建文档对象时抛出错误 其他随机性来源是磁盘文件系统损坏,不是在 i/o 中,而是在分配给 2 个不同文件的数据块中。您可以在卸载后尝试强制 fsck; fsck 通常只会检查一个干净的标志,实际上并不做任何验证。另一个想法可能是语言环境存在差异,并且不知何故,unicode BOM(字节顺序标记)被添加到文件开头。如果您在zipfile.py(或本地副本)中将import pdb;pdb.set_trace() 放在raise 异常之前,您将进入调试器并可以调查它打开了什么文件以及读取了什么。 【参考方案1】:

这两个错误都表明指定的文件不是有效的 zip 存档。所以我预计文件的写入会出现问题(通过查找和替换之前的步骤)。

我会先在写入文件后停止进程并查看文件是否存在于文件系统中以及是否可以使用 Word 手动打开它。这应该将问题一分为二并将其缩小为写作问题或阅读问题。

可能会在写入时引发错误并且它没有被捕获或其他什么,留下一个空或未刷新(打开)的文件。因此,有一种方法来监控该步骤可能是一个好主意。写入日志是您可以管理的方式。

检查出现故障的特定情况并设法重现故障将至关重要。如果这不可能,这将是一条充满猜测和失望的艰难道路。

【讨论】:

抱歉没有回复 - 如果这种情况再次发生,我要求得到通知,以便我可以在客户端深入研究它,但当然现在我正在寻找它,它没有不会再发生了:s 我们正在完成当前的部署,所以我们很可能暂时不会看到这种情况。如果我能够在客户端环境中对其进行测试,我一定会告诉你我的发现! 已经有一段时间了,但是当它在我的开发环境中随机开始发生时,我能够半可靠地重现它。我可以在 Word 中打开它抱怨的文件,所以看起来错误出在读取而不是写入上。我想知道这是否是某种序列问题,文件在尝试执行查找和替换操作之前尚未完成保存。我会尝试添加延迟来测试这个【参考方案2】:

事实证明,在这开始发生之前,最近添加了一些代码,这实际上向服务器发送了重复请求以生成相关文档。这些请求似乎是并行运行的——这令人惊讶,因为我预测冲突会更频繁地发生(使用相同的模板文件,生成的文档写入相同的目录)。

似乎如果请求序列发生在特定时间,一个请求的“查找和替换”操作将运行到另一个请求的“保存”操作。所以换句话说,我认为一个请求是试图打开一个正在保存的文档。

所以我很高兴它不是 python-docx 库更晦涩难懂的东西,这将更难确定。

【讨论】:

以上是关于python-docx:打开文件时出错-“文件头的魔数错误”/“EOFError”的主要内容,如果未能解决你的问题,请参考以下文章

python-docx 使用教程

页码python-docx

带有文件头的粘贴命令

使用python-docx处理word.docx文件

使用python-docx处理word.docx文件

Python如何操作word文档,Python-docx类库的使用