Ответ 1
Недавно я столкнулся с этой же проблемой, поэтому я впился в 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()
принимает строку, и при этом принимает это путь к файлу и создает файл-объект в этом месте. Конечным результатом является то же самое, чего мы пытаемся избежать. A PdfFileReader()
объект, который открывает файл до тех пор, пока файл не будет написан в конце!
Однако, если мы либо создадим файл-объект строки пути файла, либо объект PdfFileReader
(см. Edit 2) строки пути до того, как он будет передан в append()
, он будет автоматически создайте копию для нас как объект StringIO
, позволяя Python закрыть файл.
Я бы рекомендовал более простой merger.append(file(filename, 'rb'))
, поскольку другие сообщили, что объект PdfFileReader
может оставаться открытым в памяти даже после вызова writer.close()
.
Надеюсь, это помогло!
EDIT: Я предположил, что вы использовали PyPDF2
, а не PyPDF
. Если вы этого не сделаете, я настоятельно рекомендую переключиться, поскольку PyPDF больше не поддерживается, когда автор дает свои официальные благословения Phaseit в разработке PyPDF2.
Если по какой-то причине вы не можете поменять местами на PyPDF2 (лицензирование, системные ограничения и т.д.), чем PdfFileMerger
не будет доступен вам. В этой ситуации вы можете повторно использовать код из PyPDF2 merge
(приведенный выше) для создания копии файла как объекта StringIO
и использовать его в своем коде вместо файлового объекта.
РЕДАКТИРОВАТЬ 2: Предыдущая рекомендация использования merger.append(PdfFileReader(file(filename, 'rb')))
изменена на основе комментариев (Спасибо @Agostino).