大家好我是java1234_小锋老师。做 Java 的人只要跟 PDF 打过交道多半听过 Apache PDFBox。它不收费、文档全、社区活从抽文本、拼报表到签章、转图片基本都能扛。这两年你会发现一个挺明显的变化新项目、老项目升级大家越来越愿意直接上PDFBox 3而不是继续在 2.x 上修修补补。这篇文章就聊聊PDFBox 是什么、3.0 到底改了什么以及为什么越来越多人愿意换。先认识一下 PDFBoxApache PDFBox 是 Apache 基金会下面的开源 Java 库专门用来创建、读写、转换 PDF。它不是那种“只能预览”的工具而是能嵌进业务系统里干活的合同归档、发票生成、扫描件抽字、批量盖章这些场景都能见到它。它能做的事情大致就这几类读已有 PDF抽出文字、图片、表单字段从零创建页面写字、画线、贴图拆分、合并、加密、数字签名把页面渲染成 PNG / JPEG方便做预览2.x 用了很多年稳是稳但内存占用、IO 方式和一批过时 API 慢慢成了包袱。3.0 不是换皮而是把这些底层问题一起收拾了一遍。PDFBox 3 到底新在哪如果你只记三件事就是下面这三张图里的内容统一用 Loader 加载、增量解析省内存、IO 层重写。说人话的话大概是这样1. 加载方式换了口以前大家写PDDocument.load(...)3.0 把加载方法从PDDocument上拿掉了统一走org.apache.pdfbox.Loader。看起来只是搬家其实是把“从哪读、怎么缓存”这件事说清楚了文件、字节数组、RandomAccessRead各走各的路不再藏在一个万能load里面。2. 不再一上来就把整份 PDF 吞进内存3.0 默认做增量解析。你只访问第 1 页它就尽量只解析第 1 页相关的对象。大文件、只抽封面、只盖一页章的时候内存会老实很多。当然如果你遍历全部页面、扫全部注释该吃内存还是会吃——PDF 格式本身就是这样。3. IO 独立成模块读写更可控基础 IO 类拆到了pdfbox-io底层切到java.nio读文件支持内存映射读的时候不再依赖临时 scratch 文件。写的时候缓存策略也从老的MemoryUsageSetting换成更灵活的StreamCacheCreateFunction。另外保存时默认开启压缩普通文档体积会小一截做 PDF/A-1b 这类对压缩有限制的场景再显式关掉即可。流程可以画成这样PDF 文件 / 字节数组Loader.loadPDF增量解析只加载用到的对象抽文本 / 改内容 / 签章默认压缩保存从打开到保存整条链路比 2.x 干净不少。这也是很多人愿意升级的核心原因。为什么大家开始换过来升级库总是有成本的。大家还是换通常不是因为“版本号更大”而是这几件事正好戳中日常痛点。大文件不再那么吓人。客服系统、档案系统里动辄几十兆、上百页的扫描 PDF2.x 一加载就容易把堆打满。3.0 的增量解析和内存映射让“只读几页、只抽一段字”变得现实多了。API 更干净。2.x 里积了一堆Deprecated。3.0 直接删掉过时接口字体也改成Standard14Fonts.FontName这种更明确的写法。刚迁的时候要改几处编译错误但改完之后代码读起来不会再踩“这个方法到底还能不能用”的坑。行为更透明。以前用InputStream加载库内部会偷偷拷一份到内存或临时文件出了内存问题不好查。3.0 干脆去掉了InputStream那套重载你要内存就自己包成RandomAccessReadBuffer要落盘就走文件或内存映射。看起来更啰嗦实际上是把选择权还给你。生态已经跟上。3.0 发布后Spring 项目、各类 PDF 工具链、教程和示例都在往 3.x 靠。新同事搜到的第一篇代码往往已经是Loader.loadPDF了。继续留在 2.x维护成本会慢慢比升级还高。上手看几个真实场景说了这么多还是看代码最踏实。下面这几个例子都按 PDFBox 3 来写覆盖最常见的打开、抽字、生成。引入依赖dependencygroupIdorg.apache.pdfbox/groupIdartifactIdpdfbox/artifactIdversion3.0.4/version/dependency需要把页面渲成图片时再加一个pdfbox-tools即可。版本号按你项目里实际使用的 3.0.x 来就行。用 Loader 打开 PDF这是 2.x 迁 3.0 时改得最多的一行。文件直接丢给Loader用完自动关importjava.io.File;importorg.apache.pdfbox.Loader;importorg.apache.pdfbox.pdmodel.PDDocument;importorg.apache.pdfbox.pdmodel.PDPage;publicclassOpenPdfExample{publicstaticvoidmain(String[]args)throwsException{FilefilenewFile(report.pdf);try(PDDocumentdocumentLoader.loadPDF(file)){System.out.println(页数document.getNumberOfPages());for(PDPagepage:document.getPages()){System.out.println(页面尺寸page.getMediaBox());}}}}带密码的文件多传一个密码参数就行Loader.loadPDF(file, 123456)。如果 PDF 特别大只想映射文件、少占堆可以换成importorg.apache.pdfbox.io.RandomAccessReadMemoryMappedFile;try(PDDocumentdocumentLoader.loadPDF(newRandomAccessReadMemoryMappedFile(file))){// 按需访问页面即可}抽出正文归档检索、简单对账很多时候只是想把字拿出来importjava.io.File;importorg.apache.pdfbox.Loader;importorg.apache.pdfbox.pdmodel.PDDocument;importorg.apache.pdfbox.text.PDFTextStripper;publicclassExtractTextExample{publicstaticvoidmain(String[]args)throwsException{try(PDDocumentdocumentLoader.loadPDF(newFile(contract.pdf))){PDFTextStripperstrippernewPDFTextStripper();stripper.setStartPage(1);stripper.setEndPage(2);Stringtextstripper.getText(document);System.out.println(text);}}}只设了前两页增量解析的好处就会比较明显不必为了两页摘要把整份合同都解析完。从零生成一份 PDF3.0 里标准字体不能再写PDType1Font.HELVETICA要走Standard14Fonts。刚升级的人编译报错基本都出在这一行importjava.io.IOException;importorg.apache.pdfbox.pdmodel.PDDocument;importorg.apache.pdfbox.pdmodel.PDPage;importorg.apache.pdfbox.pdmodel.PDPageContentStream;importorg.apache.pdfbox.pdmodel.common.PDRectangle;importorg.apache.pdfbox.pdmodel.font.PDType1Font;importorg.apache.pdfbox.pdmodel.font.Standard14Fonts;publicclassCreatePdfExample{publicstaticvoidmain(String[]args)throwsIOException{try(PDDocumentdocumentnewPDDocument()){PDPagepagenewPDPage(PDRectangle.A4);document.addPage(page);try(PDPageContentStreamcsnewPDPageContentStream(document,page)){cs.beginText();cs.setFont(newPDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD),20);cs.newLineAtOffset(72,750);cs.showText(Invoice #2026-09-21);cs.endText();}// 3.0 默认压缩保存普通文档体积更小document.save(invoice.pdf);}}}中文内容的话标准 14 字体不够用需要嵌入 TTF例如PDType0Font.load(document, new File(msyh.ttf))。这一点 2.x 和 3.x 思路是一样的。如果要合并几份 PDF循环Loader.loadPDF再importPage到新文档里即可。页面预览则用PDFRenderer的renderImageWithDPI。日常这四件事——抽、建、合、渲——已经能覆盖大部分业务。写在最后PDFBox 3 没有把 PDF 处理变成一门新学科它做的是更务实的事加载入口统一、内存按需占用、IO 行为说清楚、过时 API 清干净。对还在 2.x 上的老项目迁移成本主要是load改Loader、字体构造换写法、以及不要再把InputStream直接丢进去。改完之后大文件和长期维护都会轻松一截。所以越来越多人用 PDFBox 3其实没什么神秘的它还是那个免费、能干活的 Java PDF 库只是 3.0 把该心疼内存、该把选择权交给开发者的地方都补上了。新项目直接上 3.x 就行老项目找个迭代窗口迁一次通常也值得。