Java开发 PDF文件生成方案
业务需求背景
业务端需要能够将考试答卷内容按指定格式呈现并导出为pdf格式进行存档,作为紧急需求插入。导出内容存在样式复杂性,包括特定的字体(中文)、字号、颜色,页面得有页眉、页码,数据需要进行表格聚合处理,并且需要动态处理边框、单元格、数据文本格式化等,整体功能上线时间紧迫。
第一版方案实现:前端显示 + 后端 Selenium 调用浏览器打印
为能够尽快上线此功能,团队经讨论确定第一版方案以满足需求。
实现原理 该方案核心在于借助浏览器的渲染能力,通过 Selenium 库搭配 Chrome Headless 无头浏览器模拟用户操作,具体步骤如下:
-
前端页面设计:前端开发人员根据业务需求,构建一个完整的网页模板,确保所有样式和布局都符合预期。
-
后端调用与打印:后端服务器通过Selenium库启动Chrome Headless浏览器,加载前端生成的页面链接。然后调用浏览器的打印功能,将页面转换为PDF格式并保存到指定路径供用户下载。
优点
-
快速实现:由于前端页面已经具备完善的样式和布局,后端只需负责调用和转换,因此可以较快上线。
-
充分利用现有资源:借助浏览器本身的渲染引擎,避免了额外的开发工作量。
缺点
-
性能瓶颈:每次导出都需要启动浏览器实例,消耗较多系统资源,尤其在高并发场景下容易出现性能问题。
-
潜在风险:集成第三方浏览器服务会引入额外的依赖项,从而增加系统的复杂性和不可靠性。这种外部依赖可能导致系统在面对第三方服务的故障、维护或更新时出现不稳定的情况,进而影响整体的服务质量和用户体验。
第二版方案实现:后端生成 Excel 再转成 PDF
由于存在潜在风险和性能瓶颈,需要将现有方案优化为后端生成。
具体实现
Java Excel转PDF POI+Itext5-CSDN博客
转换方案
当前市面上Excel转Pdf方案分为两类:
一:成熟的商业产品,可以直接调转换方法一键生成PDF
二:开源方案,可以写入PDF,但是不支持直接转换,也不提供转换方案,可行的方案通常为第三方自行编写的Util类开源
由于商业产品收费很高,故使用开源组件。
商业产品:aspose、spire
开源组件:itextpdf
参考文档:
Java开发中Word转PDF文件5种方案横向评测_java word转pdf-CSDN博客
Java Excel转PDF(免费) - 天航星 - 博客园
实现原理 此方案分为三个主要步骤:
-
填充 Excel 模版:将已有的Excel模版进行数据填充,写入Excel中
-
写入 Excel 文件:由于表格内容格式过于复杂,且需要根据不同数据动态合并单元格等情况,无法使用模版填充,使用Apache POI库,按照规定的格式写入Excel文件。在此过程中,需对每个单元格进行格式设置,如数据类型、对齐方式、边框、合并等,以确保数据展示规整有序。
-
转换为 PDF 文件:使用iText库将生成的Excel文件转换为PDF格式。转换时需要调整PDF页面布局,包括页面大小、边距、字体、字号、颜色等样式属性,确保最终输出符合项目要求。
优点
-
格式一致性:Excel本身具有强大的表格处理能力,能够很好地保证数据格式的一致性和准确性。
-
易于调试:在Excel中更容易发现和修正问题,可以使用Offic等软件直观查看。
-
数据模版:可以使用模版的方式改变样式布局,减少代码改动。
缺点
-
效率低下:涉及两次转换过程,增加了处理时间和资源消耗。
异常
用itext转换pdf时,如果单元格内容过多,会出现该bug
com.itextpdf.text.DocumentException: java.lang.NullPointerException: Cannot read field \"llx\" because \"cell\" is null
在互联网中未出现的bug,经过研究后无法修复,但是目前市场上的成熟转换方案都是商业产品,免费或使用版本限制太多,无法满足需求,改用直接写入pdf方案。
解决方案
com.itextpdf.text.DocumentException: java.lang.NullPointerException: Cannot read field \“llx\“ becau-CSDN博客
第三版方案实现:纯后端 PDF 生成
由于上述 bug 经多人研究解决及替换方案均无果,只能改用直接写入 PDF 的方案。
实现步骤:
代码替换:由于原本方案实现的布局代码已经完善,数据构造和布局填充是分离的,使用新方案只需要修改poi处代码,改用itext的方式重新写入即可
避坑
-
单元格合并时机:使用 POI 方式时,代码逻辑为先填充表格全部单元格内容,最后判断单元格进行合并。在 iText 方案中,此逻辑会导致合并单元格跨页时,下一页合并单元格丢失效果。经研究发现,需在创建合并单元格的第一个单元格时就指定合并区域,余下被合并单元格不再写入 PdfPTable。
-
分批次写入document:当一次写入内容过多时,依然会抛出关于 “llx” 的 bug。需减少一次写入 document 的单元格数量,目前方案是每道题作为一个新的 PdfPTable,处理完成就写入一次 document,而非整张试卷一次性写入。