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 заявление. Так что каждый открытый файл правильно закрыт.

Другие вопросы по тегам