web-dev-qa-db-fra.com

pypdf Fusion de plusieurs fichiers pdf en un seul pdf

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?

25
daydaysay

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).

56
Rejected

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.

3
Patrick Maupin

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()
0
sgillis

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é. 

0
flyingfoxlee