ich vor kurzem über dieses genau das gleiche Problem kam, so grub ich in PyPDF2, um zu sehen, was los ist und wie es zu lösen.
Hinweis: Ich gehe davon aus, dass filename
eine wohlgeformte Datei Pfad Zeichenfolge ist. Angenommen, das gleiche für all meinen Code
Die kurze Antwort
Verwenden Sie die PdfFileMerger()
Klasse anstelle der PdfFileWriter()
Klasse. Ich habe versucht, die folgenden zur Verfügung zu stellen, wie eng Ihre Inhalte ähneln, wie ich kann:
from PyPDF2 import PdfFileMerger, PdfFileReader
[...]
merger = PdfFileMerger()
for filename in filenames:
merger.append(PdfFileReader(file(filename, 'rb')))
merger.write("document-output.pdf")
Die lange Antwort
Die Art und Weisen Sie PdfFileReader
und PdfFileWriter
verwenden wird jede Datei offen zu halten, und schließlich verursacht Python IOError 24 zu generieren. Um genauer zu sein, wenn Sie eine Seite zu PdfFileWriter
hinzufügen, fügen Sie Verweise auf die Seite in der geöffneten PdfFileReader
(daher der angegebene IO-Fehler, wenn Sie die Datei schließen). Python erkennt die Datei, auf die noch verwiesen wird, und führt keine Speicherbereinigung/automatische Dateischließung durch, obwohl das Dateihandle wiederverwendet wird. Sie bleiben geöffnet, bis PdfFileWriter
nicht mehr Zugriff auf sie benötigt, die in Ihrem Code output.write(outputStream)
ist.
Um dies zu lösen, erstellen Sie Kopien im Speicher des Inhalts, und ermöglichen Sie die Datei geschlossen werden. Ich habe in meinen Abenteuern durch den PyPDF2-Code festgestellt, dass die Klasse PdfFileMerger()
diese Funktionalität bereits besitzt. Anstatt das Rad neu zu erfinden, entschied ich mich dafür, es stattdessen zu verwenden. Ich habe jedoch gelernt, dass mein erster Blick auf PdfFileMerger
nicht nahe genug war, und dass es unter bestimmten Bedingungen nur Kopien erstellt.
Meine ersten Versuche sahen wie folgt aus, und wurden in der gleichen IO resultierenden Probleme:
merger = PdfFileMerger()
for filename in filenames:
merger.append(filename)
merger.write(output_file_path)
am Code PyPDF2 Quelle Sehen, sehen wir, dass append()
erfordert fileobj
weitergegeben werden, und verwendet dann die merge()
Funktion, übergibt die letzte Seite als neue Dateiposition. merge()
führt Folgendes mit fileobj
(bevor es mit PdfFileReader(fileobj)
Eröffnung:
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
Wir können sehen, dass die append()
Option einen String akzeptiert, und wenn sie dies tun, nimmt es ein Dateipfad ist und erstellt eine Datei Objekt an dieser Stelle Das Endergebnis ist genau dasselbe, was wir zu vermeiden versuchen: Ein PdfFileReader()
Objekt, das eine Datei geöffnet hat, bis die Datei schließlich geschrieben wird!
Wenn wir jedoch entweder ein Dateiobjekt der Dateipfadzeichenfolge oder ein PdfFileReader
(siehe Edit 2) Objekt des Pfadstrings vor wird es in append()
übergeben, es wird automatisch eine Kopie für uns als StringIO
Objekt erstellen, so dass Python die Datei schließen kann.
Ich würde die einfachere merger.append(file(filename, 'rb'))
empfehlen, wie andere berichtet haben, dass ein PdfFileReader
Objekt im Speicher offen bleiben kann, auch nach dem Aufruf writer.close()
.
Hoffe das half!
EDIT: Ich nahm an, dass Sie PyPDF2
, nicht PyPDF
verwendeten. Wenn dies nicht der Fall ist, empfehle ich dringend, zu wechseln, da PyPDF nicht mehr vom Autor betreut wird, der Phaseit bei der Entwicklung von PyPDF2 seinen offiziellen Segen gibt.
Wenn Sie aus irgendeinem Grund nicht zu PyPDF2 (Lizenzierung, Systemeinschränkungen usw.) wechseln können, steht Ihnen PdfFileMerger
nicht zur Verfügung. In diesem Fall können Sie den Code von PyPDF2s merge
-Funktion (wie oben angegeben) erneut verwenden, um eine Kopie der Datei als StringIO
-Objekt zu erstellen und diese in Ihrem Code anstelle des Dateiobjekts zu verwenden.
EDIT 2: vorherige Empfehlung von merger.append(PdfFileReader(file(filename, 'rb')))
mit geänderten basierend auf Kommentare (Danke @Agostino).
Ich werde ehrlich sein; Ich habe die lange Antwort nicht gelesen. Die kurze Antwort war großartig. – BeReal82
Ich bemerkte, dass ich einige der angefügten Dateien nicht löschen konnte, indem ich ein intermediäres 'PdfFileReader'-Objekt mit dem Aufruf' writer.append (PdfFileReader (Datei (Dateiname, 'rb')))) erstellte. Sie bleiben auch nach dem Aufruf von 'writer.close()' gesperrt. Der einfachere Aufruf 'merger.append (Datei (Dateiname, 'rb'))' scheint nicht das gleiche Problem zu haben. – Agostino
Würde dies nicht zu einem Speicherproblem führen, wenn die Dateien zu groß sind? – Nishant