我最近遇到了同样的问题,所以我研究了 PyPDF2 以了解发生了什么以及如何解决它。
注意:我假设这filename
是一个格式正确的文件路径字符串。假设我的所有代码都一样
简短的回答
使用PdfFileMerger()
类而不是PdfFileWriter()
类。我已尝试提供以下内容以尽可能接近您的内容:
from PyPDF2 import PdfFileMerger, PdfFileReader
[...]
merger = PdfFileMerger()
for filename in filenames:
merger.append(PdfFileReader(file(filename, 'rb')))
merger.write("document-output.pdf")
长答案
您使用PdfFileReader
并保持每个文件打开的方式,并最终导致PdfFileWriter
Python 生成 IOError 24。更具体地说,当您向PdfFileWriter
.PdfFileReader
关闭文件时出错)。Python 检测到该文件仍被引用,并且尽管重新使用文件句柄,但不会执行任何垃圾收集/自动文件关闭。它们保持打开状态,直到PdfFileWriter
不再需要访问它们,这output.write(outputStream)
在您的代码中。
要解决此问题,请在内容的内存中创建副本,并允许关闭文件。我在冒险中通过 PyPDF2 代码注意到PdfFileMerger()
该类已经具有此功能,因此我没有重新发明轮子,而是选择使用它。不过,我了解到,我最初的观察PdfFileMerger
还不够接近,而且它只在某些条件下创建了副本。
我最初的尝试如下所示,并导致了相同的 IO 问题:
merger = PdfFileMerger()
for filename in filenames:
merger.append(filename)
merger.write(output_file_path)
查看 PyPDF2 源代码,我们看到append()
需要fileobj
传递,然后使用该merge()
函数,将其最后一页作为新文件位置传递。merge()
执行以下操作fileobj
(在使用以下命令打开之前PdfFileReader(fileobj)
:
if type(fileobj) in (str, unicode):
fileobj = file(fileobj, 'rb')
my_file = True
elif type(fileobj) == file:
fileobj.seek(0)
filecontent = fileobj.read()
fileobj = StringIO(filecontent)
my_file = True
elif type(fileobj) == PdfFileReader:
orig_tell = fileobj.stream.tell()
fileobj.stream.seek(0)
filecontent = StringIO(fileobj.stream.read())
fileobj.stream.seek(orig_tell)
fileobj = filecontent
my_file = True
我们可以看到该append()
选项确实接受一个字符串,并且在这样做时假定它是一个文件路径并在该位置创建一个文件对象。最终结果与我们试图避免的事情完全相同。一个PdfFileReader()
对象保持打开文件,直到文件最终被写入!
但是,如果我们在传递到之前创建文件路径字符串的文件对象或路径字符串的PdfFileReader
(参见编辑 2)对象,它将自动为我们创建一个副本作为对象,允许 Python 关闭文件.append()
StringIO
我会推荐更简单的merger.append(file(filename, 'rb'))
,因为其他人报告说PdfFileReader
对象可能在内存中保持打开状态,即使在调用writer.close()
.
希望这有帮助!
编辑:我假设您使用的是PyPDF2
,而不是PyPDF
。如果你不是,我强烈建议切换,因为 PyPDF 不再维护,作者在开发 PyPDF2 时给予 Phaseit 官方祝福。
如果由于某种原因您无法切换到 PyPDF2(许可、系统限制等)PdfFileMerger
,那么您将无法使用。在这种情况下,您可以重新使用 PyPDF2merge
函数(如上提供)中的代码来创建文件的副本作为StringIO
对象,并在代码中使用它来代替文件对象。
编辑 2:以前的使用建议merger.append(PdfFileReader(file(filename, 'rb')))
根据评论更改(感谢@Agostino)。