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

PyPDF2压缩PDF无效:Streamlit嵌入PDF超2MB限制问题

解决Streamlit中PDF压缩无法达标(小于2MB)的问题

你当前用PyPDF2的compress_content_streams()方法压缩PDF效果不佳,核心原因是这个方法仅能压缩PDF的文本内容流(比如合并重复的绘图指令),对PDF中占体积大头的高分辨率图片几乎没有压缩作用。要把4.5MB的PDF压到2MB以内,必须针对图片做处理,下面提供两种可行方案:

方案一:用PyMuPDF(fitz)处理图片压缩(推荐)

PyMuPDF对PDF内的图片有更强的压缩能力,可以调整图片分辨率、降低画质,能大幅压缩文件体积。

压缩函数实现

import fitz
from io import BytesIO

def compress_pdf_with_pymupdf(pdf_file, target_size=2*1024*1024, initial_quality=50, downsample_res=150):
    # 读取上传的PDF字节
    pdf_bytes = pdf_file.getvalue()
    doc = fitz.open("pdf", pdf_bytes)
    
    # 定义压缩图片的内部函数
    def process_images(doc, quality, res):
        for page in doc:
            image_list = page.get_images(full=True)
            for img in image_list:
                xref = img[0]
                base_image = doc.extract_image(xref)
                image_bytes = base_image["image"]
                image_ext = base_image["ext"]
                
                # 重新调整图片分辨率并压缩质量
                img_doc = fitz.open(image_ext, image_bytes)
                img_doc.set_dpi(res, res)
                compressed_img = img_doc.convert_to_image(quality=quality).tobytes()
                
                # 替换原PDF中的图片
                doc.update_image(xref, compressed_img)
        return doc
    
    # 第一次压缩
    doc = process_images(doc, initial_quality, downsample_res)
    output = BytesIO()
    doc.save(output, deflate=True, garbage=3)  # garbage=3清理所有冗余对象
    doc.close()
    
    # 如果第一次压缩后仍超标,逐步降低画质重试
    current_size = len(output.getvalue())
    quality = initial_quality
    while current_size > target_size and quality > 10:
        quality -= 10
        doc = fitz.open("pdf", pdf_bytes)
        doc = process_images(doc, quality, downsample_res)
        output = BytesIO()
        doc.save(output, deflate=True, garbage=3)
        doc.close()
        current_size = len(output.getvalue())
    
    return output.getvalue()

Streamlit调用示例

import streamlit as st

uploaded_file = st.sidebar.file_uploader("Upload a file", key= "uploaded_file")

if uploaded_file is not None:
    compressed_pdf_bytes = compress_pdf_with_pymupdf(uploaded_file)
    compressed_size = len(compressed_pdf_bytes) / 1024 / 1024
    
    if compressed_size <= 2:
        st.success(f"压缩完成!当前大小:{compressed_size:.2f}MB")
        # 嵌入PDF(Streamlit 1.28+支持pdf_viewer)
        st.pdf_viewer(compressed_pdf_bytes)
        # 提供下载按钮
        st.download_button(
            label="下载压缩后的PDF",
            data=compressed_pdf_bytes,
            file_name="compressed_pdf.pdf",
            mime="application/pdf"
        )
    else:
        st.error(f"压缩后仍为{compressed_size:.2f}MB,超过2MB限制,请尝试上传更小的文件")

方案二:优化PyPDF2的压缩逻辑(仅适用于文本为主的PDF)

如果你的PDF以文本为主,图片很少,可以通过移除冗余内容、启用更高压缩级别来提升效果:

from PyPDF2 import PdfReader, PdfWriter
from io import BytesIO

def compress_pdf_pypdf2_advanced(pdf_file):
    pdf_reader = PdfReader(pdf_file)
    pdf_writer = PdfWriter()
    
    for page in pdf_reader.pages:
        # 压缩内容流
        page.compress_content_streams()
        # 移除页面注释(如果有的话)
        if "/Annots" in page:
            del page["/Annots"]
        # 移除无用的资源
        if "/Resources" in page:
            resources = page["/Resources"]
            for key in list(resources.keys()):
                if key not in ["/Font", "/XObject"]:
                    del resources[key]
        pdf_writer.add_page(page)
    
    # 启用最高压缩级别写入
    output_pdf = BytesIO()
    pdf_writer.write(output_pdf, compression_level=6)
    return output_pdf.getvalue()

关键说明

  • 若PDF包含大量高清图片,优先用方案一,PyMuPDF的图片处理能力是压缩体积的核心。
  • 方案二仅能小幅压缩文本型PDF,对图片多的文件效果有限。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 05:07:17