从 47MB 压缩到 8MB,PDF压缩工具的架构设计思路【PDF压缩工具02】
-
代码是怎么组织的?
-
为什么要这么设计?
今天就来拆解一下这个工具的内部结构,看看数据怎么流动、代码怎么组织。内容偏技术一些,但保证你能听懂。
两个核心数据结构
工具里定义了两个@dataclass,专门用来存放配置和结果,分工明确,互不干扰。
第一个是 CompressConfig,用来装压缩参数。mode、dpi、quality、grayscale 这些都在里面,还有一个进度回调函数progress_cb,后面会用到。
这个类的validate()方法会在压缩前做参数检查,比如 mode 必须是'smart'、'raster'或'split'之一,dpi 范围是 1 到 600,quality 是 1 到 100。不合法就抛异常,在传入前就能发现问题
config = CompressConfig( mode='smart', dpi=150, quality=75, grayscale=False, progress_cb=my_callback,)config.validate() # 参数不对直接报错
第二个是 CompressResult,用来装执行结果。不管成功还是失败,这个对象都会返回。它的字段包含了原始大小、压缩后大小、压缩率、页数、耗时、错误信息。如果成功了,__str__会输出友好的格式化字符串;如果失败了,记录错误原因,方便排查
result = PDFCompressor.compress('a.pdf', 'b.pdf')# 成功时打印:[OK] a.pdf [smart] 12.34 MB -> 3.21 MB (saved 74.0%) | 50 pages | 2.3s# 失败时打印:[FAIL] a.pdf [smart] File does not exist: a.pdf
这种设计的好处是:调用方不用 try/except,只看 result.success 就能判断结果,所有信息都在一个对象里,不用到处拼字符串。
PDFCompressor 类的设计
类的设计思路是单一职责 + 对外统一。三个公开方法分别对应三种操作:
PDFCompressor.compress(...) # 单文件压缩PDFCompressor.compress_batch(...) # 批量压缩PDFCompressor.split(...) # 按页拆分
内部实现全部交给三个私有函数:
_compress_smart() — Smart 模式_compress_raster() — Raster 模式_split_pdf() — Split 模式(内部直接调了 PDFCompressor.split)
公开接口永远返回CompressResult,内部错误全部捕获,转换成result.success=False,不会让异常泄漏出去。这个原则贯彻得很彻底:调用者永远不需要处理 try/except,只需要看返回值。
错误处理的哲学
这一点值得单独说一下。
大多数工具在遇到文件找不到、PDF 损坏这类情况时会直接抛异常,调用方必须写一堆 try/except 来兜底。这个工具不是这样——它会在compress()的入口处做参数校验,把错误接住,包装成result.success=False,然后正常返回给调用。
try: config.validate() _validate_input(input_path)except (FileNotFoundError, ValueError) as e:return CompressResult( success=False, input_path=input_path, output_path=None, mode=mode, orig_size_mb=_file_size_mb(input_path), error=str(e), )
这样写调用方代码就非常干净,不用担心异常遗漏。
总结一下
整个工具的架构非常扁平:
-
数据模型: CompressConfig+CompressResult,一个装配置,一个装结果 -
核心类: PDFCompressor,三个公开方法 + 三个私有实现 -
设计原则:错误不泄漏、参数必校验、返回必完整
理解了这个结构,下一篇我们来看 Smart 模式的压缩逻辑——它是怎么做到“只压图像、不动文字“的。
喜欢小居的文章,点赞、关注、转发。点个“在看”支持一下! 👇
有问题评论区或后台找小居!
PDF压缩工具(软件桌面版)已放入合集《桌面端PDF工具箱》。
PDF压缩工具,1秒压缩支持文件拖拽批量处理,3种模式满足各种PDF文件
关注评论“PDF压缩”,免费领取“PDF压缩工具(.exe)”
感谢支持,PDF软件工具持续更新中!
夜雨聆风