为了集中阅读课程讲义,我做过一个在线 PDF 合并工具。服务现已下线,但它涉及的文件顺序、页面复制和失败清理,仍然适合用来说明文件处理服务的实现思路。
顺序是接口的一部分
合并需要一个有序的输入列表,而不是依赖目录枚举或 HashMap 的遍历顺序。文件名中的 10 排在 2 前面,也不一定符合用户预期,应让用户显式确认次序。
下面使用 Python 3 和 pypdf,输入两个独立文件,不覆盖任何源文件:
from pathlib import Path
from pypdf import PdfWriter
def merge_pdf(inputs, output):
sources = [Path(name).resolve() for name in inputs]
destination = Path(output).resolve()
if not sources:
raise ValueError("at least one input is required")
if destination in sources or destination.exists():
raise ValueError("output must be a new file, separate from inputs")
writer = PdfWriter()
try:
for source in sources:
writer.append(str(source))
with destination.open("xb") as stream:
writer.write(stream)
finally:
writer.close()
merge_pdf(["1.pdf", "2.pdf"], "merged.pdf")
示例要求安装 pypdf,目录和文件需要提前准备。写入失败可能留下不完整输出,服务化时应先写独立临时文件,校验成功后再原子发布结果,失败产物由任务目录统一清理。
页面复制不等于所有 PDF 语义都保持
Java 项目也可以通过匹配版本的 PdfCopy 等 API 复制页面,但必须关闭 reader、writer 和输出流,并在最终写入完成后才报告成功。表单、书签、附件与数字签名需要单独验证,不能用页数正确推断全部信息无损。
加密 PDF 需要合法取得密码并处理解密失败。不要在代码中硬编码密码,也不应对无法读取的文件静默跳过后仍标记整个任务成功。
上传服务还需要任务边界
每次任务使用隔离目录和服务端生成的文件名,限制文件数量、大小、处理时间及并发。客户端选择顺序与服务端接收顺序分开保存,合并输出完成后再提供下载,并设置过期清理。
至少验证总页数、首尾页面、输入顺序、不同纸张尺寸与损坏文件。遇到任一失败时,用户应能知道哪个输入没有处理,而不是拿到一份悄悄少页的结果。
参考:pypdf 合并文档。