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,而非整张试卷一次性写入。
相关文章:

Java开发 PDF文件生成方案
业务需求背景 业务端需要能够将考试答卷内容按指定格式呈现并导出为pdf格式进行存档,作为紧急需求插入。导出内容存在样式复杂性,包括特定的字体(中文)、字号、颜色,页面得有页眉、页码,数据需要进行表格聚…...

数学期望和方差
数学期望(Mathematical Expectation)和方差(Variance)是概率论和统计学中两个非常重要的概念。下面将分别对这两个概念进行解释。 数学期望 数学期望是随机变量的平均值,它描述了随机变量的中心位置。对于离散随机变…...

【面试AI算法题中的知识点】方向涉及:ML/DL/CV/NLP/大数据...本篇介绍Tensor RT 的优化流程。
【面试AI算法题中的知识点】方向涉及:ML/DL/CV/NLP/大数据…本篇介绍Tensor RT 的优化流程。 【面试AI算法题中的知识点】方向涉及:ML/DL/CV/NLP/大数据…本篇介绍Tensor RT 的优化流程。 文章目录 【面试AI算法题中的知识点】方向涉及:ML/D…...

BLDC无感控制的驱动逻辑
如何知道转子已经到达预定位置,因为我们只有知道了转子到达了预定位置之后才能进行换相,这样电机才能顺滑的运转。转子位置检测常用的有三种方式。 方式一:通过过零检测,三相相电压与电机中性点电压进行比较。过零检测的优点在于…...
BP神经网络的反向传播算法
BP神经网络(Backpropagation Neural Network)是一种常用的多层前馈神经网络,通过反向传播算法进行训练。反向传播算法的核心思想是通过计算损失函数对每个权重的偏导数,从而调整权重,使得网络的预测输出与真实输出之间…...

[实用指南]如何将视频从iPhone传输到iPad
概括 将视频从 iPhone 传输到 iPad 时遇到问题?您可能知道一种方法,但不知道如何操作。此外,您要传输的视频越大,完成任务就越困难。那么如何将视频从 iPhone 传输到 iPad,特别是当您需要发送大视频文件时?…...

Linux Snipaste 截图闪屏/闪烁
防 csdn 不能看,Go to juejin Linux Snipaste 截图时窗口元素一闪一闪的无法正常使用。 解决此问题时系统环境为 Manjaro KDE6,不过我在其他发行版与 gnome 上也碰到了。 先放解决办法: # 启动 Snipaste 时去掉缩放参数 env -u QT_SCREEN_…...

【YOLOv5】源码(common.py)
该文件位于/models/common.py,提供了构建YOLOv5模型的各种基础模块,其中包含了常用的功能模块,如自动填充autopad函数、标准卷积层Conv、瓶颈层Bottleneck、C3、SPPF、Concat层等 参考笔记:【YOLOv3】 源码(common.py…...

Node 如何生成 RSA 公钥私钥对
一、引入crypto模块 crypto 为node 自带模块,无需安装 const crypto require(crypto);二、封装生成方法 async function generateRSAKeyPair() {return new Promise((resolve, reject) > {crypto.generateKeyPair(rsa, {modulusLength: 2048, // 密钥长度为 …...

瑞_Linux中部署配置Java服务并设置开机自启动
文章目录 背景Linux服务配置步骤并设置开机自启动附-Linux服务常用指令 🙊 前言:由于博主在工作时,需要将服务部署到 Linux 服务器上运行,每次通过指令启动服务非常麻烦,所以将 jar 包部署的服务设置开机自启动&#x…...

javaEE-多线程进阶-JUC的常见类
juc:指的是java.util.concurrent包,该包中加载了一些有关的多线程有关的类。 目录 一、Callable接口 FutureTask类 参考代码: 二、ReentrantLock 可重入锁 ReentrantLock和synchronized的区别: 1.ReentantLock还有一个方法:…...

Flume拦截器的实现
Flume conf文件编写 vim file_to_kafka.conf#定义组件 a1.sources r1 a1.channels c1#配置source a1.sources.r1.type TAILDIR a1.sources.r1.filegroups f1 a1.sources.r1.filegroups.f1 /Users/zhangjin/model/project/realtime-flink/applog/log/app.* # 设置断点续传…...

Swift Combine 学习(四):操作符 Operator
Swift Combine 学习(一):Combine 初印象Swift Combine 学习(二):发布者 PublisherSwift Combine 学习(三):Subscription和 SubscriberSwift Combine 学习(四&…...

leetcode 173.二叉搜索树迭代器栈绝妙思路
以上算法题中一个比较好的实现思路就是利用栈来进行实现,以下方法三就是利用栈来进行实现的,思路很好,很简练。进行next的时候,先是一直拿到左边的子树,直到null为止,这一步比较好思考一点,下一…...

df.groupby([pd.Grouper(freq=‘1M‘, key=‘Date‘), ‘Buyer‘]).sum()
df.groupby([pd.Grouper(freq1M, keyDate), Buyer]).sum() 用于根据特定的时间频率和买家(Buyer)对 DataFrame 进行分组,然后计算每个分组的总和。下面是对这行代码的逐步解释: df.groupby([...]):这个操作会根据传入的…...

LLM - 使用 LLaMA-Factory 部署大模型 HTTP 多模态服务 (4)
欢迎关注我的CSDN:https://spike.blog.csdn.net/ 本文地址:https://spike.blog.csdn.net/article/details/144881432 大模型的 HTTP 服务,通过网络接口,提供 AI 模型功能的服务,允许通过发送 HTTP 请求,交互…...

icp备案网站个人备案与企业备案的区别
个人备案和企业备案是在进行ICP备案时需要考虑的两种不同情况。个人备案是指个人拥有的网站进行备案,而企业备案则是指企业或组织名下的网站进行备案。这两者在备案过程中有一些明显的区别。 首先,个人备案相对来说流程较为简单。个人备案只需要提供个人…...

如何不修改模型参数来强化大语言模型 (LLM) 能力?
前言 如果你对这篇文章感兴趣,可以点击「【访客必读 - 指引页】一文囊括主页内所有高质量博客」,查看完整博客分类与对应链接。 大语言模型 (Large Language Model, LLM, e.g. ChatGPT) 的参数量少则几十亿,多则上千亿,对其的训…...

AF3 AtomAttentionEncoder类的init_pair_repr方法解读
AlphaFold3 的 AtomAttentionEncoder 类中,init_pair_repr 方法方法负责为原子之间的关系计算成对表示(pair representation),这是原子转变器(atom transformer)模型的关键组成部分,直接影响对蛋白质/分子相互作用的建模。 init_pair_repr源代码: def init_pair_repr(…...

DDoS攻击防御方案大全
1. 引言 随着互联网的迅猛发展,DDoS(分布式拒绝服务)攻击成为了网络安全领域中最常见且危害严重的攻击方式之一。DDoS攻击通过向目标网络或服务发送大量流量,导致服务器过载,最终使其无法响应合法用户的请求。本文将深…...

Vue中常用指令
一、内容渲染指令 1.v-text:操作纯文本,用于更新标签包含的文本,但是使用不灵活,无法拼接字符串,会覆盖文本,可以简写为{{}},{{}}支持逻辑运算。 用法示例: //把name对应的值渲染到…...

Servlet解析
概念 Servlet是运行在服务端的小程序(Server Applet),可以处理客户端的请求并返回响应,主要用于构建动态的Web应用,是SpringMVC的基础。 生命周期 加载和初始化 默认在客户端第一次请求加载到容器中,通过反射实例化…...

带虚继承的类对象模型
文章目录 1、代码2、 单个虚继承3、vbptr是什么4、虚继承的多继承 1、代码 #include<iostream> using namespace std;class Base { public:int ma; };class Derive1 :virtual public Base { public:int mb; };class Derive2 :public Base { public:int mc; };class Deri…...

深度学习中的离群值
文章目录 深度学习中有离群值吗?深度学习中的离群值来源:处理离群值的策略:1. 数据预处理阶段:2. 数据增强和鲁棒模型:3. 模型训练阶段:4. 异常检测集成模型: 如何处理对抗样本?总结…...

如何利用Logo设计免费生成器创建专业级Logo
在当今的商业世界中,一个好的Logo是品牌身份的象征,它承载着公司的形象与理念。设计一个专业级的Logo不再需要花费大量的金钱和时间,尤其是当我们拥有Logo设计免费生成器这样的工具时。接下来,让我们深入探讨如何利用这些工具来创…...

Mysql SQL 超实用的7个日期算术运算实例(10k)
文章目录 前言1. 加上或减去若干天、若干月或若干年基本语法使用场景注意事项运用实例分析说明2. 确定两个日期相差多少天基本语法使用场景注意事项运用实例分析说明3. 确定两个日期之间有多少个工作日基本语法使用场景注意事项运用实例分析说明4. 确定两个日期相隔多少个月或多…...

运算指令(PLC)
加 ADD 减 SUB 乘 MUL 除 DIV 浮点运算 整数运算...

「Mac畅玩鸿蒙与硬件49」UI互动应用篇26 - 数字填色游戏
本篇教程将带你实现一个数字填色小游戏,通过简单的交互逻辑,学习如何使用鸿蒙开发组件创建趣味性强的应用。 关键词 UI互动应用数字填色动态交互逻辑判断游戏开发 一、功能说明 数字填色小游戏包含以下功能: 数字选择:用户点击…...

机器学习经典算法——逻辑回归
目录 算法介绍 算法概念 算法的优缺点 LogisticRegression()函数理解 环境准备 算法练习 算法介绍 算法概念 逻辑回归(Logistic Regression)是一种广泛应用于分类问题的机器学习算法。 它基于线性回归的思想,但通过引入一个逻辑函数&…...

【数据仓库金典面试题】—— 包含详细解答
大家好,我是摇光~,用大白话讲解所有你难懂的知识点 该篇面试题主要针对面试涉及到数据仓库的数据岗位。 以下都是经典的关于数据仓库的问题,希望对大家面试有用~ 1、什么是数据仓库?它与传统数据库有何区别? 数据仓库…...