使用 PyPDF2 连接内存中的 PDF 文件
Concatenating PDF files in memory with PyPDF2
我希望在纯 python 的内存中有效地将一堆小的 pdf 连接(附加)在一起。具体来说,通常的情况是500个单页pdf,每个大小在400kB左右,合并为一个。假设 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
)连接以下代码。然而,情况似乎并非如此,最后一行的调试语句表明使用的最大内存为 almost 700 MB。此外,使用 Mac os x 资源监视器,分配的内存在到达最后一行时指示为 600 MB。
运行 gc.collect()
将其减少到 350 MB(almos 太好了?)。在这种情况下,为什么我必须手动 运行 垃圾收集来摆脱合并垃圾?我已经看到这(可能)导致内存在稍微不同的情况下累积,我现在将跳过。
import io
import resource # For debugging
from PyPDF2 import PdfFileMerger
def merge_pdfs(iterable):
"""Merge pdfs in memory"""
merger = 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)
问题总结
- 为什么上面的代码需要 almost 700 MB 的内存来合并 200 MB 的 pdf? 400 MB 以上的开销还不够吗?我该如何优化它?
- 为什么我需要 运行 手动垃圾收集来摆脱 PyPDF2 合并垃圾,而问题变量应该已经超出范围?
- 这个通用方法怎么样?这种情况下 BytesIO 适合使用吗?
merger.write(myio)
似乎 运行 有点慢,因为所有这些都发生在 ram 中。
谢谢!
问:为什么上面的代码需要将近 700 MB 的内存来合并 200 MB 的 pdf? 400 MB 以上的开销还不够吗?如何优化?
答:因为 .append
创建了一个新的流对象,然后你使用 merger.write(myio)
,它创建了另一个流对象,你的内存中已经有 200 MB 的 pdf 文件,所以 3*200 MB。
问:为什么我需要手动 运行 垃圾收集来摆脱 PyPDF2 合并垃圾,而问题变量应该已经超出范围?
答:在PyPDF2中是已知的issue。
问:这个通用方法怎么样?在这种情况下,BytesIO 适合使用吗?
A:考虑到内存问题,您可能想尝试不同的方法。也许一个一个合并,暂时把文件存到磁盘,然后把已经合并的从内存中清空。
PyMuPdf library 也可以很好地替代 PyPDF2
.
中 PDFMerger
的性能问题
我希望在纯 python 的内存中有效地将一堆小的 pdf 连接(附加)在一起。具体来说,通常的情况是500个单页pdf,每个大小在400kB左右,合并为一个。假设 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
)连接以下代码。然而,情况似乎并非如此,最后一行的调试语句表明使用的最大内存为 almost 700 MB。此外,使用 Mac os x 资源监视器,分配的内存在到达最后一行时指示为 600 MB。
运行 gc.collect()
将其减少到 350 MB(almos 太好了?)。在这种情况下,为什么我必须手动 运行 垃圾收集来摆脱合并垃圾?我已经看到这(可能)导致内存在稍微不同的情况下累积,我现在将跳过。
import io
import resource # For debugging
from PyPDF2 import PdfFileMerger
def merge_pdfs(iterable):
"""Merge pdfs in memory"""
merger = 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)
问题总结
- 为什么上面的代码需要 almost 700 MB 的内存来合并 200 MB 的 pdf? 400 MB 以上的开销还不够吗?我该如何优化它?
- 为什么我需要 运行 手动垃圾收集来摆脱 PyPDF2 合并垃圾,而问题变量应该已经超出范围?
- 这个通用方法怎么样?这种情况下 BytesIO 适合使用吗?
merger.write(myio)
似乎 运行 有点慢,因为所有这些都发生在 ram 中。
谢谢!
问:为什么上面的代码需要将近 700 MB 的内存来合并 200 MB 的 pdf? 400 MB 以上的开销还不够吗?如何优化?
答:因为 .append
创建了一个新的流对象,然后你使用 merger.write(myio)
,它创建了另一个流对象,你的内存中已经有 200 MB 的 pdf 文件,所以 3*200 MB。
问:为什么我需要手动 运行 垃圾收集来摆脱 PyPDF2 合并垃圾,而问题变量应该已经超出范围?
答:在PyPDF2中是已知的issue。
问:这个通用方法怎么样?在这种情况下,BytesIO 适合使用吗?
A:考虑到内存问题,您可能想尝试不同的方法。也许一个一个合并,暂时把文件存到磁盘,然后把已经合并的从内存中清空。
PyMuPdf library 也可以很好地替代 PyPDF2
.
PDFMerger
的性能问题