2025终极指南:Java Web开发中如何优雅地生成、预览与下载PDF?
Meta描述: 深入探讨Java Web开发中PDF处理的完整流程,从技术选型(iText, Apache PDFBox, Flying Saucer)到代码实战,涵盖生成、预览、下载等核心场景,助你轻松实现服务器端PDF文档功能。

引言:为什么Java Web开发离不开PDF?
在当今的企业级应用中,PDF(Portable Document Format)格式因其跨平台、不易篡改、排版美观等特性,成为报告生成、合同打印、数据导出、电子发票等场景下的标准格式,作为一名Java Web开发者,掌握在Web应用中高效、稳定地生成和处理PDF文档,是一项至关重要的技能。
你是否曾面临这样的需求:
- 将用户填写的表单数据导出为一份格式规范的PDF合同?
- 将系统生成的月度/年度报表一键下载为PDF文件?
- 将HTML页面(如订单详情页)原封不动地转换为PDF进行存档?
本文将为你提供一个从入门到精通的【Java Web开发PDF】终极解决方案,手把手教你从零开始,实现服务器端PDF文档的生成、预览与下载。
核心技术选型:Java PDF处理库大盘点
在Java生态中,处理PDF的库琳琅满目,选择合适的工具是项目成功的第一步,以下是业界最主流的几个选择,各有侧重:

| 库名称 | 类型 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| iText | 商业/开源(AGPL) | 功能强大,API成熟,商业版支持更多高级功能(如数字签名、AcroForms) | 开源版(AGPL)有商业限制,学习曲线较陡 | 需要生成复杂文档、表格、表单的商业项目 |
| Apache PDFBox | 纯开源(Apache 2.0) | 完全免费,无任何限制,功能全面,能处理现有PDF | API相对底层,操作复杂度较高 | 需要免费解决方案,或需要对现有PDF进行读写、解析 |
| Flying Saucer (xhtmlrenderer) | 开源 | 核心优势:将HTML/CSS直接渲染为PDF,极大简化了复杂排版 | 依赖CSS支持,对CSS 3.0支持有限,对复杂JavaScript支持不佳 | 将Web页面、HTML模板快速转换为PDF,如发票、对账单 |
| OpenPDF | 开源 | 基于iText 5分支,解决了iText AGPL的许可证问题 | 功能更新可能稍慢于iText | 需要iText 5风格API,且希望规避AGPL许可证的项目 |
【专家建议】:
- 如果需求是“从零创建”,且文档格式复杂(大量表格、水印、中文字体),iText是首选。
- 如果预算有限,或需要“修改现有PDF”,Apache PDFBox是可靠的免费之选。
- 如果需求是“把HTML页面变成PDF”,Flying Saucer是你的不二法门,能为你节省大量排版时间。
实战演练:在Spring Boot中生成并下载PDF
我们将以最流行的Spring Boot框架为例,结合iText,演示一个完整的“生成PDF并下载”的流程。
步骤1:创建Spring Boot项目并添加依赖
在pom.xml中添加iText 7的依赖(iText 7采用模块化设计,核心模块为kernel和pdf)。

<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext7-core</artifactId>
<version>7.2.5</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext-asian</artifactId>
<version>5.2.0</version>
</dependency>
<!-- itext-asian 用于支持中文等亚洲语言 -->
步骤2:创建PDF工具类
创建一个PdfUtils,用于封装PDF生成的核心逻辑。
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.element.Table;
import com.itextpdf.layout.properties.TextAlignment;
import org.springframework.stereotype.Component;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@Component
public class PdfUtils {
public byte[] generateOrderPdf(String orderNumber, String customerName, String productName, double price) throws IOException {
// ByteArrayOutputStream 用于在内存中生成PDF,避免写入磁盘
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// 1. 创建 PdfWriter 和 PdfDocument 对象
PdfWriter writer = new PdfWriter(baos);
PdfDocument pdf = new PdfDocument(writer);
// 2. 创建 Document 对象,用于添加内容
Document document = new Document(pdf);
// 3. 添加内容
document.add(new Paragraph("订单详情").setFontSize(18).setBold().setTextAlignment(TextAlignment.CENTER));
document.add(new Paragraph(" ").setMarginTop(10)); // 添加一个空行作为间距
// 创建一个两列表格
Table table = new Table(2);
table.addCell("订单号:");
table.addCell(orderNumber);
table.addCell("客户姓名:");
table.addCell(customerName);
table.addCell("商品名称:");
table.addCell(productName);
table.addCell("订单金额:");
table.addCell(String.valueOf(price));
document.add(table);
// 4. 关闭Document,写入PDF数据到ByteArrayOutputStream
document.close();
return baos.toByteArray();
}
}
步骤3:创建Controller处理HTTP请求
创建一个PdfController,提供一个接口来触发PDF生成和下载。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
@RestController
@RequestMapping("/api/pdf")
public class PdfController {
@Autowired
private PdfUtils pdfUtils;
@GetMapping("/download")
public ResponseEntity<byte[]> downloadPdf(@RequestParam String orderNumber) {
try {
// 模拟从数据库获取数据
byte[] pdfBytes = pdfUtils.generateOrderPdf(orderNumber, "张三", "Java编程思想", 128.00);
// 设置响应头
String filename = "订单-" + orderNumber + ".pdf";
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_PDF);
// Content-Disposition 是关键,它告诉浏览器这是一个附件,并建议文件名
headers.setContentDispositionFormData("attachment", filename, StandardCharsets.UTF_8);
return ResponseEntity.ok()
.headers(headers)
.body(pdfBytes);
} catch (IOException e) {
// 返回错误信息
return ResponseEntity.internalServerError().body("生成PDF失败: ".getBytes());
}
}
}
步骤4:测试
启动你的Spring Boot应用,访问 http://localhost:8080/api/pdf/download?orderNumber=20250520001,浏览器就会自动下载一个名为“订单-20250520001.pdf”的文件。
进阶场景:HTML转PDF的优雅实现
对于需要动态生成、包含丰富样式的文档,直接使用iText代码排版会非常痛苦,这时,Flying Saucer就能大显身手。
步骤1:添加依赖
<dependency>
<groupId>org.xhtmlrenderer</groupId>
<artifactId>flying-saucer-pdf-itext5</artifactId>
<version>9.1.22</version>
</dependency>
步骤2:准备HTML模板
在resources/templates目录下创建一个invoice.html文件。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">发票</title>
<style>
body { font-family: 'SimSun', 'STSong', serif; }
.header { text-align: center; margin-bottom: 30px; }
.invoice-info { display: flex; justify-content: space-between; margin-bottom: 20px; }
table { width: 100%; border-collapse: collapse; }
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
th { background-color: #f2f2f2; }
.footer { margin-top: 50px; text-align: right; }
</style>
</head>
<body>
<div class="header">
<h1>销售发票</h1>
</div>
<div class="invoice-info">
<div>
<p>发票号码: ${invoiceNumber}</p>
</div>
<div>
<p>开票日期: ${invoiceDate}</p>
</div>
</div>
<table>
<thead>
<tr>
<th>商品名称</th>
<th>数量</th>
<th>单价</th>
<th>总价</th>
</tr>
</thead>
<tbody>
<tr>
<td>Java Web开发实战</td>
<td>1</td>
<td>99.00</td>
<td>99.00</td>
</tr>
<tr>
<td>Spring Boot权威指南</td>
<td>2</td>
<td>89.50</td>
<td>179.00</td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="3" style="text-align: right;"><strong>合计:</strong></td>
<td><strong>278.00</strong></td>
</tr>
</tfoot>
</table>
<div class="footer">
<p>感谢您的惠顾!</p>
</div>
</body>
</html>
步骤3:创建HTML转PDF的工具类
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.xhtmlrenderer.pdf.ITextRenderer;
import org.springframework.stereotype.Component;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
@Component
public class HtmlToPdfUtils {
@Autowired
private ResourceLoader resourceLoader;
public byte[] generateFromHtml(String templateName, Map<String, Object> data) throws IOException {
// 1. 加载HTML模板
Resource resource = resourceLoader.getResource("classpath:templates/" + templateName);
try (InputStream is = resource.getInputStream()) {
String html = new String(is.readAllBytes(), "UTF-8");
// 2. 简单的模板数据替换(实际项目中可用Thymeleaf等模板引擎)
for (Map.Entry<String, Object> entry : data.entrySet()) {
html = html.replace("${" + entry.getKey() + "}", entry.getValue().toString());
}
// 3. 使用Flying Saucer生成PDF
ITextRenderer renderer = new ITextRenderer();
renderer.setDocumentFromString(html);
renderer.layout();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
renderer.createPDF(baos);
return baos.toByteArray();
}
}
}
常见问题与最佳实践
-
中文乱码问题:
- 原因:iText或PDFBox默认不包含中文字体。
- 解决方案:引入一个中文字体文件(如
simhei.ttf),并在创建Document或PdfFont时注册该字体。// iText 7 示例 PdfFont font = PdfFontFactory.createFont("src/main/resources/fonts/SimHei.ttf", BaseEncoding.IDENTITY_H, true); document.add(new Paragraph("你好,世界!").setFont(font));
-
服务器性能与内存:
- 问题:生成大型或复杂PDF会消耗大量内存。
- 最佳实践:尽量使用
ByteArrayOutputStream在内存中操作,完成后立即返回响应并释放内存,对于超大型PDF,考虑分块生成或使用流式处理。
-
安全性与权限:
- 问题:恶意用户可能通过构造特殊数据(如超长字符串)进行DoS攻击。
- 最佳实践:对用户输入进行校验和限制,对生成的PDF文件名进行过滤,防止路径遍历攻击。
-
预览与下载:
- 下载:如上文所示,设置
Content-Disposition为attachment。 - 预览:将
Content-Disposition设置为inline,并确保浏览器有内置的PDF阅读器(如Chrome, Firefox),或者,将PDF文件上传到对象存储(如OSS),然后返回一个可访问的URL,让前端<iframe>或<a>标签直接打开。
- 下载:如上文所示,设置
总结与展望
Java Web开发中处理PDF是一项兼具挑战与价值的任务,通过本文,我们系统地了解了从iText、PDFBox到Flying Saucer等主流技术,并通过代码实战掌握了生成、下载、HTML转PDF等核心技能。
随着云原生和微服务架构的普及,未来PDF处理可能会更多地以微服务的形式存在,为多个业务系统提供统一的文档生成能力,结合OCR(光学字符识别)和NLP(自然语言处理),对PDF文档进行智能解析和信息提取,也将是一个充满潜力的方向。
希望这篇【Java Web开发PDF】指南能为你提供切实的帮助,如果你有更多的问题或独特的解决方案,欢迎在评论区交流分享!
【SEO关键词布局】:
- 主关键词:Java Web开发PDF
- 长尾关键词:Java生成PDF、Spring Boot PDF、iText教程、Java导出PDF、HTML转PDF Java、Java PDF下载、Java PDF库、Java报表PDF
- 语义化标签:
<h1>,<h2>,<h3>,<code>,<pre>,<strong>等均已合理使用。
