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
相关产品推荐
相关产品推荐

