如果我有1000多个pdf文件需要合并成一个pdf,
from PyPDF2 import PdfReader, PdfWriter
writer = PdfWriter()
for i in range(1000):
filepath = f"my/pdfs/{i}.pdf"
reader = PdfReader(open(filepath, "rb"))
for page in reader.pages:
writer.add_page(page)
with open("document-output.pdf", "wb") as fh:
writer.write(fh)
执行以上代码,当 reader = PdfReader(open(filepath, "rb"))
,
报错信息: IOError: [Errno 24] Too many open files:
我认为这是一个错误,如果不是,我该怎么办?
原文由 daydaysay 发布,翻译遵循 CC BY-SA 4.0 许可协议
我最近遇到了这个完全相同的问题,所以我深入研究了 PyPDF2 以了解发生了什么,以及如何解决它。
注意:我假设
filename
是格式正确的文件路径字符串。假设我所有的代码都一样简短的回答
使用
PdfFileMerger()
类而不是PdfFileWriter()
类。我已尝试提供以下内容,以尽可能接近您的内容:长答案
您使用
PdfFileReader
和PdfFileWriter
的方式是保持每个文件打开,并最终导致 Python 生成 IOError 24。更具体地说,当您将页面添加到PdfFileWriter
,您正在添加对打开页面的引用PdfFileReader
(因此,如果您关闭文件,则会出现 IO 错误)。 Python 检测到该文件仍被引用,并且尽管重新使用了文件句柄,但不执行任何垃圾收集/自动文件关闭。它们保持打开状态,直到PdfFileWriter
不再需要访问它们,它位于output.write(outputStream)
在您的代码中。要解决此问题,请在内存中创建内容副本,并允许关闭文件。我在 PyPDF2 代码的冒险中注意到
PdfFileMerger()
类已经具有此功能,因此我没有重新发明轮子,而是选择使用它。不过,我了解到,我最初对PdfFileMerger
的了解还不够接近,它只 在特定条件下 创建了副本。我最初的尝试如下所示,并导致了相同的 IO 问题:
查看 PyPDF2 源代码,我们看到
append()
需要fileobj
被传递,然后使用merge()
作为新页面传递的最后一个函数–文件位置。merge()
执行以下操作fileobj
(在使用PdfFileReader(fileobj)
打开它之前:我们可以看到
append()
选项确实接受一个字符串,并且在这样做时,假定它是一个文件路径并在该位置创建一个文件对象。最终结果与我们试图避免的完全相同。一个PdfFileReader()
对象保持打开文件直到文件最终被写入!但是,如果我们创建文件路径字符串的文件对象 或
PdfFileReader
_(请参阅编辑 2)_路径字符串对象, 然后再将 其传递到append()
,它将自动创建作为StringIO
对象的副本,允许 Python 关闭文件。我会推荐更简单的
merger.append(file(filename, 'rb'))
,因为其他人报告说PdfFileReader
对象可能在内存中保持打开状态,即使在调用writer.close()
希望这有帮助!
编辑: 我假设你使用的是
PyPDF2
,而不是PyPDF
。如果你不是,我强烈建议切换,因为 PyPDF 不再维护,作者在开发 PyPDF2 时给予 Phaseit 官方祝福。如果出于某种原因您不能切换到 PyPDF2(许可、系统限制等),
PdfFileMerger
将不可用。在这种情况下,您可以重新使用 PyPDF2 的merge
函数(上面提供)中的代码来创建文件的副本作为StringIO
对象,并在您的代码中使用它文件对象的。编辑 2: 以前的使用建议
merger.append(PdfFileReader(file(filename, 'rb')))
根据评论更改 (感谢@Agostino) 。