必须设 license,否则 unipdf 合并 PDF 会 panic;pdfcpu 免 license 但丢书签;大文件需流式 io.Copy;文件名排序须自然序;三类边界问题(空/损坏文件、页码越界、内容错位)必须校验。

unipdf 合并 PDF 必须设 license,否则 panic
不设 license 就直接调 unipdf/v4 的合并函数,运行时会立刻 panic 并报 “license not set”。这不是可忽略的 warning,是硬性拦截:默认只允许处理 ≤3 页的 PDF,第 4 页起就崩溃。
实操建议:
- 去 官网注册邮箱,申请免费开发许可,下载
pdf.license文件 - 代码中必须用
license.NewLicenseFromBytes()加载,不能跳过、不能 mock、不能注释 - 别回退到
v2分支——旧版已无法正确读取加密 PDF 或渲染文字,且社区版在v3+中彻底移除
pdfcpu 合并快但丢书签,outline 不是 bug 是缺失
用 pdfcpu 调 api.MergeFiles() 确实免 license、无 CGO、适合容器部署,但它对 outline(书签)完全不支持。输入 PDF 带多级目录,合并后全消失,且不报任何 warning。
常见误判:
立即学习“go语言免费学习笔记(深入)”;
- 以为是代码漏调 API,实际是
pdfcpu v0.3.14根本没实现 outline 合并逻辑 - 如果必须保留书签结构,只能切回
unipdf:先用PDFReader.ReadOutline()读每个文件的 outline,再用PDFWriter.AddOutlineEntry()手动重建,并修正Dest.PageNum偏移量 - 注意 macOS 预览对 >3 级书签自动折叠,不是代码问题,换 Chrome 或 Acrobat 验证才准
大文件合并别读全内存,io.Copy 是唯一稳解
用 os.ReadFile 或 io.ReadAll 合并 GB 级 PDF,90% 会触发 runtime: out of memory;手动 Write + Seek 容易错位,导致文件损坏。
正确做法就是流式搬运:
- 目标文件用
os.Create(dstPath)(覆盖)或os.OpenFile(dstPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)(追加) - 每个源文件用
os.Open(srcPath),直接传给io.Copy(dst, src),中间不建[]byte缓冲 - 文件名排序必须自然序:
part_1.pdf、part_10.pdf、part_2.pdf用sort.Strings会排成 1→10→2,得用extractNum提数字再比大小
三类边界问题不校验,上线必崩
本地跑通不代表生产可用。以下三类问题常静默失败:
- 空文件或损坏 PDF:
os.Stat().Size > 0不等于文件有效,必须调pdfcpu.ValidateFile()或unipdf.NewReader()检查 error - 页码越界不报错:
pdfcpu.ExtractPages("10-20")在只有 5 页的文件里会静默返回空结果,得提前用pdfcpu.GetNumPages()校验范围 - 合并后内容错位或截断:必须检查
io.Copy返回值,合并完调dst.Sync()确保落盘,再用哈希(如sha256)比对源文件总和与目标文件是否一致



















