You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PIL合并JPG图片生成PDF时脚本中途自动终止的解决方案

故障根因

你判断的没错,脚本跑一半被自动终止就是内存占满触发系统OOM(内存不足)查杀机制了。现有代码的核心问题是把所有resize完的图片对象全堆在im_list里驻留内存,等所有图都处理完才一次性写入PDF,图片量稍大时内存占用会随图片数线性上涨,最终超出系统可用阈值被强制结束。
另外还有两处问题会额外推高内存占用:

  • 图片、PDF读写对象用完未主动释放,依赖Python自动垃圾回收会导致内存无法及时回收
  • PIL的save是同步阻塞方法,执行返回时文件已经落盘,后面写的循环轮询文件是否存在的逻辑完全冗余,白占系统资源
修复方法
  • 禁止全量缓存图片对象:不要把所有处理完的图片存在列表里等最后一次性写入,改为逐张/逐小批次追加写入PDF,处理完的图片立刻释放对应内存。如果要处理的图片量特别大(大几百上千张),可以先把每张图转成单页临时PDF存在本地磁盘,最后统一合并后删除临时文件,内存占用能压到单张图片的水平。
  • 主动释放资源:所有图片、PDF读写操作优先用with上下文管理器编写,出作用域自动关闭句柄释放内存;手动创建的对象用完主动调用close()方法,避免内存泄漏。
  • 平衡速度和内存:逐张追加写入PDF的速度偏慢,可以调整为每攒20~30张处理好的图片就追加写入一次,不用攒全量,也不用每张都重新打开PDF文件。
参考修正代码
import os
import glob
import time
from PIL import Image
from PyPDF2 import PdfFileMerger, PdfFileReader

# 用os.path.join拼接路径,避免不同系统斜杠兼容问题
imageList = sorted(glob.glob(os.path.join(root, files, '*.jpg')))
exist_pdf_list = glob.glob(os.path.join(root, "*", "*.pdf"))
noImages = len(imageList)
factorL = 0.25
factorP = 0.5
start = time.time()

# 处理第一页作为PDF基础页
with Image.open(imageList[0]) as first_im:
    w, h = first_im.size
    new_size = (int(w*factorP), int(h*factorP)) if h > w else (int(w*factorL), int(h*factorL))
    base_im = first_im.resize(new_size).convert("RGB")

namePath = os.path.join(rootPDF, f"{pdf_id}.pdf")
# 逐张处理后续页面,不攒全量列表
append_buffer = []
for idx in range(1, noImages):
    img_path = imageList[idx]
    print(f"处理进度: {idx+1}/{noImages},当前文件: {img_path}")
    with Image.open(img_path) as im:
        w, h = im.size
        new_size = (int(w*factorP), int(h*factorP)) if h > w else (int(w*factorL), int(h*factorL))
        resized_im = im.resize(new_size).convert("RGB")
        append_buffer.append(resized_im)
        # 攒够20张就写一次,平衡内存和速度
        if len(append_buffer) >= 20 or idx == noImages -1:
            base_im.save(
                namePath,
                "PDF",
                quality=40,
                save_all=True,
                append_images=append_buffer
            )
            # 写完清空缓存,释放内存
            for im_item in append_buffer:
                im_item.close()
            append_buffer.clear()
base_im.close()

# 合并已有PDF,用完主动关闭merger对象
if exist_pdf_list:
    exist_pdf_list.append(namePath)
    merger = PdfFileMerger()
    try:
        for pdf_path in exist_pdf_list:
            with open(pdf_path, 'rb') as f:
                merger.append(PdfFileReader(f))
        with open(namePath, 'wb') as new_file:
            merger.write(new_file)
    finally:
        merger.close()

# 写操作日志
with open('pdfcreated_log.txt', 'a', encoding='utf-8') as f:
    f.write(f"{pdf_id}\n")

cost = time.time() - start
print(f"处理完成,总耗时: {round(cost, 2)}s")

内容的提问来源于stack exchange,提问作者Martha

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 20:09:10