2013-06-14 2 views
24

Если у меня есть 1000+ PDF файлы должны быть объединены в один PDF,pypdf Объединение нескольких файлов в один 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:

Я думаю, что это ошибка, если нет, то что я должен делать?

ответ

48

Недавно я наткнулся на это точно такая же проблема, так что я порылся в 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(), открывающий файл до тех пор, пока файл не будет написан в конце!

Однако, если мы либо создадим файл-объект строки пути файла , либо a PdfFileReader(см. Редактировать 2) объект строки пути до он передается в append(), он автоматически создаст для нас копию как объект StringIO, что позволит Python закрыть файл.

Я бы рекомендовал более простой merger.append(file(filename, 'rb')), так как другие сообщили, что объект PdfFileReader может оставаться открытым в памяти даже после звонка writer.close().

Надеюсь, это помогло!

EDIT: Я предположил, что вы использовали PyPDF2, а не PyPDF. Если вы этого не сделаете, я настоятельно рекомендую переключиться, поскольку PyPDF больше не поддерживается, когда автор дает свои официальные благословения Phaseit в разработке PyPDF2.

Если по какой-либо причине вы не можете поменять местами на PyPDF2 (лицензирование, системные ограничения и т. Д.), То PdfFileMerger будет недоступен для вас. В этой ситуации вы можете повторно использовать код из функции PyPDF2 merge (см. Выше), чтобы создать копию файла как объект StringIO и использовать его в своем коде вместо файлового объекта.

EDIT 2: Предыдущая рекомендация использования merger.append(PdfFileReader(file(filename, 'rb'))) изменяется на основе комментариев (Спасибо @Agostino).

+0

Буду честным; Я не прочел длинный ответ. Короткий ответ был велик. – BeReal82

+1

Я заметил, что не удалось удалить некоторые из добавленных файлов, создав промежуточный объект «PdfFileReader» с вызовом 'writer.append (PdfFileReader (файл (имя файла, 'rb'))). Они остаются заблокированными даже после вызова 'writer.close()'. Более простой вызов 'merger.append (file (filename, 'rb'))', похоже, не имеет такой же проблемы. – Agostino

+1

Не будет ли это проблемой памяти, если файлы слишком большие? – Nishant

0

это может быть только то, что он говорит, вы о pening для многих файлов. Вы можете явно использовать f=file(filename) ... f.close() в цикле или использовать оператор with. Чтобы каждый открытый файл был правильно закрыт.

0

Проблема в том, что вам разрешено открывать определенное количество файлов в любой момент времени. Есть способы изменить это (http://docs.python.org/3/library/resource.html#resource.getrlimit), но я не думаю, что вам это нужно.

Что вы могли бы попробовать закрывает файлы в цикл:

input = PdfFileReader() 
output = PdfFileWriter() 
for file in filenames: 
    f = open(file, 'rb') 
    input = PdfFileReader(f) 
    # Some code 
    f.close() 
+2

если используется f.close(), exec output.write (outputStream), запросить ошибку ввода-вывода. – daydaysay

1

Пакет pdfrw читает каждый файл за один раз, поэтому не будет страдать от проблемы с слишком большим количеством открытых файлов. Here - пример сценария конкатенации.

Соответствующая часть - предполагает inputs список входных имен файлов и outfn это имя выходного файла:

from pdfrw import PdfReader, PdfWriter 

writer = PdfWriter() 
for inpfn in inputs: 
    writer.addpages(PdfReader(inpfn).pages) 
writer.write(outfn) 

Отказ от ответственности: Я основной автор pdfrw.

Смежные вопросы