Si j'ai plus de 1000 fichiers pdf doivent être fusionnés en un seul 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()
Exécutez le code ci-dessus , quand input = PdfFileReader(file(filename500+, "rb"))
,
Un message d'erreur : IOError: [Errno 24] Too many open files:
Je pense que c'est un bug, sinon, que dois-je faire?
Je suis récemment tombé sur le même problème et je me suis donc tourné vers PyPDF2 pour voir ce qui se passait et comment le résoudre.
Note: Je suppose que filename
est une chaîne de chemin de fichier bien formée. Supposons la même chose pour tout mon code
La réponse courte
Utilisez la classe PdfFileMerger()
au lieu de la classe PdfFileWriter()
. J'ai essayé de fournir les éléments suivants pour ressembler le plus possible à votre contenu:
from PyPDF2 import PdfFileMerger, PdfFileReader
[...]
merger = PdfFileMerger()
for filename in filenames:
merger.append(PdfFileReader(file(filename, 'rb')))
merger.write("document-output.pdf")
La réponse longue
La façon dont vous utilisez PdfFileReader
et PdfFileWriter
conserve chaque fichier ouvert et permet finalement à Python de générer IOError 24. Plus précisément, lorsque vous ajoutez une page à la PdfFileWriter
, vous ajoutez des références à la page dans la variable PdfFileReader
ouverte ( d'où l'erreur IO signalée si vous fermez le fichier). Python détecte que le fichier est toujours référencé et n'effectue aucune récupération de place/fermeture automatique de fichier malgré la réutilisation du descripteur de fichier. Ils restent ouverts jusqu'à ce que PdfFileWriter
n'en ait plus besoin, ce qui correspond à output.write(outputStream)
dans votre code.
Pour résoudre ce problème, créez des copies en mémoire du contenu et autorisez la fermeture du fichier. Lors de mes aventures dans le code PyPDF2, j'ai remarqué que la classe PdfFileMerger()
avait déjà cette fonctionnalité. Au lieu de réinventer la roue, j'ai donc choisi de l'utiliser. J’ai cependant appris que j’avais jeté un regard d’origine sur PdfFileMerger
et qu’il ne créait que des copies dans certaines conditions.
Mes tentatives initiales ressemblaient à ce qui suit et donnaient lieu aux mêmes IO problèmes:
merger = PdfFileMerger()
for filename in filenames:
merger.append(filename)
merger.write(output_file_path)
En regardant le code source de PyPDF2, nous voyons que append()
requiert la transmission de fileobj
, puis utilise la fonction merge()
, en transmettant sa dernière page en tant que nouvelle position des fichiers. merge()
effectue les opérations suivantes avec fileobj
(avant de l'ouvrir avec 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
Nous pouvons voir que l’option append()
accepte une chaîne et, ce faisant, suppose qu’il s’agit d’un chemin de fichier et crée un objet fichier à cet emplacement. Le résultat final est exactement la même chose que nous essayons d'éviter. Un objet PdfFileReader()
contenant un fichier ouvert jusqu'à ce que le fichier soit finalement écrit!
Cependant, si nous faisons un objet fichier de la chaîne de chemin de fichier ou un PdfFileReader
(voir Edit 2) objet de la chaîne de chemin d'accès avant que il soit passé à append()
, il créera automatiquement une copie pour nous sous la forme d'un objet StringIO
, permettant à Python de fermer le fichier.
Je recommanderais la merger.append(file(filename, 'rb'))
plus simple, car d'autres ont signalé qu'un objet PdfFileReader
peut rester ouvert en mémoire, même après l'appel de writer.close()
.
J'espère que cela a aidé!
EDIT: J'ai supposé que vous utilisiez PyPDF2
, pas PyPDF
. Si vous ne l'êtes pas, je vous recommande vivement de changer de logiciel, car PyPDF n'est plus maintenu et l'auteur donne ses bénédictions officielles à Phaseit pour le développement de PyPDF2.
Si, pour une raison quelconque, vous ne pouvez pas passer à PyPDF2 (licences, restrictions système, etc.), PdfFileMerger
ne sera pas disponible. Dans ce cas, vous pouvez réutiliser le code de la fonction merge
de PyPDF2 (fournie ci-dessus) pour créer une copie du fichier sous la forme d'un objet StringIO
et l'utiliser dans votre code à la place de l'objet fichier.
EDIT 2: Recommandation précédente d'utilisation de merger.append(PdfFileReader(file(filename, 'rb')))
modifiée en fonction des commentaires (Merci @Agostino).
Le paquet pdfrw lit chaque fichier en une fois et ne souffrira donc pas du problème du trop grand nombre de fichiers ouverts. Ici est un exemple de script de concaténation.
La partie pertinente - suppose que inputs
est une liste de noms de fichiers en entrée et outfn
est un nom de fichier de sortie:
from pdfrw import PdfReader, PdfWriter
writer = PdfWriter()
for inpfn in inputs:
writer.addpages(PdfReader(inpfn).pages)
writer.write(outfn)
Disclaimer: Je suis l'auteur principal de pdfrw.
Le problème est que vous n'êtes autorisé à avoir qu'un certain nombre de fichiers ouverts à un moment donné. Il existe des moyens de changer cela ( http://docs.python.org/3/library/resource.html#resource.getrlimit ), mais je ne pense pas que vous en ayez besoin.
Ce que vous pourriez essayer, c’est de fermer les fichiers dans la boucle for:
input = PdfFileReader()
output = PdfFileWriter()
for file in filenames:
f = open(file, 'rb')
input = PdfFileReader(f)
# Some code
f.close()
C’est peut-être ce qu’il indique, vous ouvrez de nombreux fichiers . Vous pouvez utiliser explicitement f=file(filename) ... f.close()
dans la boucle ou utiliser l’instruction with
. Pour que chaque fichier ouvert soit correctement fermé.