pypdf Объединение нескольких файлов PDF в один PDF
Если у меня есть 1000+ PDF-файлы должны быть объединены в один PDF,
input = PdfFileReader()
output = PdfFileWriter()
filename0000 ----- filename 1000
input = PdfFileReader(file(filename, "rb"))
pageCount = input.getNumPages()
for iPage in range(0, pageCount):
output.addPage(input.getPage(iPage))
outputStream = file("document-output.pdf", "wb")
output.write(outputStream)
outputStream.close()
Выполните приведенный выше код ,, когда input = PdfFileReader(file(filename500+, "rb"))
,
Сообщение об ошибке:IOError: [Errno 24] Too many open files:
Я думаю, что это ошибка, если нет, что мне делать?
4 ответа
Недавно я столкнулся с точно такой же проблемой, поэтому я заглянул в 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
не было достаточно близко, и что он только создавал копии в определенных условиях.
Мои первоначальные попытки выглядели следующим образом и приводили к тем же проблемам ввода-вывода:
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()
option принимает строку и при этом предполагает, что это путь к файлу, и создает объект файла в этом месте. Конечный результат - это то же самое, что мы пытаемся избежать. PdfFileReader()
объект держит открытый файл, пока файл не будет записан!
Однако, если мы сделаем объект файла из строки пути к файлу или PdfFileReader
(см. Редактировать 2) объект строки пути до того, как он будет передан в append()
, он автоматически создаст копию для нас как StringIO
объект, позволяющий Python закрыть файл.
Я бы порекомендовал более простой merger.append(file(filename, 'rb'))
, как другие сообщили, что PdfFileReader
объект может оставаться открытым в памяти даже после вызова writer.close()
,
Надеюсь, это помогло!
РЕДАКТИРОВАТЬ: я предполагал, что вы используете PyPDF2
не PyPDF
, Если нет, я настоятельно рекомендую переключиться, поскольку PyPDF больше не поддерживается, поскольку автор дает официальные благословения Phaseit при разработке PyPDF2.
Если по какой-либо причине вы не можете перейти на PyPDF2 (лицензирование, системные ограничения и т. Д.), Чем PdfFileMerger
не будет доступен для вас. В этой ситуации вы можете повторно использовать код из PyPDF2 merge
функция (приведенная выше) для создания копии файла в виде StringIO
объект, и используйте это в своем коде вместо объекта файла.
РЕДАКТИРОВАТЬ 2: Предыдущая рекомендация использования merger.append(PdfFileReader(file(filename, 'rb')))
изменено на основе комментариев (спасибо @Agostino).
Я написал этот код, чтобы помочь с ответом:-
import sys
import os
import PyPDF2
merger = PyPDF2.PdfFileMerger()
#get PDFs files and path
path = sys.argv[1]
pdfs = sys.argv[2:]
os.chdir(path)
#iterate among the documents
for pdf in pdfs:
try:
#if doc exist then merge
if os.path.exists(pdf):
input = PyPDF2.PdfFileReader(open(pdf,'rb'))
merger.append((input))
else:
print(f"problem with file {pdf}")
except:
print("cant merge !! sorry")
else:
print(f" {pdf} Merged !!! ")
merger.write("Merged_doc.pdf")
В этом случае я использовал PyPDF2.PdfFileMerger и PyPDF2.PdfFileReader вместо явного преобразования имени файла в объект файла.
Пакет pdfrw считывает каждый файл за один раз, поэтому не будет страдать от проблемы слишком большого количества открытых файлов. Вот пример сценария конкатенации.
Соответствующая часть - предполагает inputs
список входных имен файлов, и outfn
имя выходного файла:
from pdfrw import PdfReader, PdfWriter
writer = PdfWriter()
for inpfn in inputs:
writer.addpages(PdfReader(inpfn).pages)
writer.write(outfn)
Отказ от ответственности: я основной автор pdfrw.
Проблема в том, что вам разрешено иметь только определенное количество файлов, открытых в любой момент времени. Есть способы изменить это ( http://docs.python.org/3/library/resource.html), но я не думаю, что вам это нужно.
Что вы можете попробовать, так это закрыть файлы в цикле for:
input = PdfFileReader()
output = PdfFileWriter()
for file in filenames:
f = open(file, 'rb')
input = PdfFileReader(f)
# Some code
f.close()
Это может быть просто то, что он говорит, вы открываете для многих файлов. Вы можете явно использовать f=file(filename) ... f.close()
в цикле, или используйте with
заявление. Так что каждый открытый файл правильно закрыт.