PyPDF2:在内存中连接pdfs

时间:2017-08-13 16:58:39

标签: python pdf memory pypdf2

我希望在纯python中将一堆小pdf有效地连接在内存中。具体来说,通常的情况是500个单页pdf,每个都有大约400 kB的大小,要合并为一个。让我们说pdf在内存中是可迭代的,比如列表:

my_pdfs = [pdf1_fileobj, pdf2_fileobj, ..., pdfn_fileobj]  # type is BytesIO

每个pdf_fileobj的类型为BytesIO。然后,基本内存使用量约为200 MB(500 pdf,每个400kB)。

理想情况下,我希望以下代码连接使用总共不超过400-500 MB的内存(包括my_pdfs)。但是,似乎并非如此,最后一行的调试语句表明最大内存过去几乎是700 MB。此外,使用Mac os x资源监视器,分配的内存在到达最后一行时指示为600 MB。

运行gc.collect()会将此减少到350 MB(几乎太好了?)。为什么我必须手动运行垃圾收集以摆脱合并垃圾,在这种情况下?我已经看到这个(可能)导致内存在稍微不同的情况下积累,我现在就跳过了。

import PyPDF2
import io
import resources  # For debugging

def merge_pdfs(iterable):
    ''' Merge pdfs in memory '''
    merger = PyPDF2.PdfFileMerger()
    for pdf_fileobj in iterable:
        merger.append(pdf_fileobj)

    myio = io.BytesIO()
    merger.write(myio)
    merger.close()

    myio.seek(0)
    return myio

my_concatenated_pdf = merge_pdfs(my_pdfs)

# Print the maximum memory usage
print('Memory usage: %s (kB)' % resource.getrusage(resource.RUSAGE_SELF).ru_maxrss)

问题摘要

  • 为什么上面的代码需要几乎700 MB的内存来合并200 MB的pdfs?不应该400 MB +开销就够了吗?我该如何优化它?
  • 当有问题的变量已经超出范围时,为什么我需要手动运行垃圾收集以摆脱PyPDF2合并垃圾?
  • 这种一般方法怎么样? BytesIO适合使用是这种情况吗? merger.write(myio)似乎确实有点慢,因为所有事情都发生在公羊身上。

谢谢!

2 个答案:

答案 0 :(得分:1)

问:为什么上面的代码需要几乎700 MB的内存来合并200 MB的pdfs?不应该400 MB +开销就足够了吗?我该如何优化它?

答:因为.append创建了一个新的流对象,然后你使用了merger.write(myio),它创建了另一个流对象,你已经在内存中有200 MB的pdf文件,所以3 * 200 MB。

问:当有问题的变量已经超出范围时,为什么我需要手动运行垃圾收集以摆脱PyPDF2合并垃圾?

答:PyPDF2中已知issue

问:这种一般方法怎么样? BytesIO是否适合使用?

答:考虑到内存问题,您可能想尝试不同的方法。也许可以逐个合并,暂时将文件保存到磁盘,然后从内存中清除已经合并的文件。

答案 1 :(得分:0)

PyMuPdf library也可能是PDFMergerPyPDF2的性能问题的一个不错的选择。