本文主要是介绍SpringBoot集成iText快速生成PDF教程,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
《SpringBoot集成iText快速生成PDF教程》本文介绍了如何在SpringBoot项目中集成iText9.4.0生成PDF文档,包括新特性的介绍、环境准备、Service层实现、Contro...
SpringBoot集成iText 9.4.0生成PDF
2025年iText已发展到9.x版本,本文将带你体验最新特性和最佳实践
在现代企业应用开发中,动态生成PDF文档是一个常见需求。无论是生成报表、发票、合同还是其他业务单据,PDF格式因其跨平台、保持布局不变的特性而备受青睐。
本文将详细介绍如何在SpringBoot项目中集成iText 9.4.0——当前最新的版本,来实现高效、专业的PDF生成。
一、iText 9新特性与架构变革
iText 9.x相比旧版本进行了彻底的重构:
二、环境准备与依赖配置
Maven依赖配置
在SpringBoot项目的pom.XML中添加以下依赖:
<properties>
<itext.version>9.4.0</itext.version>
</properties>
<dependencies>
<!-- iText 9 核心模块 -->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>kernel</artifactId>
<version>${itext.version}</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>io</artifactId>
<version>${itext.version}</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>layout</artifactId>
<version>${itext.version}</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>forms</artifactId>
<version>${itext.version}</version>
</dependency>
<!-- 条形码支持 -->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>barcodes</artifactIjsd>
<version>${itext.version}</version>
</dependency>
<!-- 中文支持 -->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>font-asian</artifactId>
<version>${itext.version}</version>
</dependency>
</dependencies>
三、完整的Service层实现
1. 基础PDF服务
@Service
public class PdfService {
/**
* 创建简单PDF文档
*/
public void createSimplePdf(OutputStream outputStream) throws IOException {
PdfWriter writer = new PdfWriter(outputStream);
PdfDocument pdfDoc = new PdfDocument(writer);
Document document = new Document(pdfDoc);
// 添加内容
document.add(new Paragraph("Hello, iText 9.4.0!")
.setFontSize(20)
.setBold());
document.add(new Paragraph("这是一个使用SpringBoot和iText 9生成的PDF文档"));
document.add(new Paragraph("生成时间: " + new Date()));
// 关闭文档
document.close();
}
/**
* 创建带表格的PDF
*/
public void createTablePdf(OutputStream outputStream) throws IOException {
PdfWriter writer = new PdfWriter(outputStream);
PdfDocument pdfDoc = new PdfDocument(writer);
Document document = new Document(pdfDoc);
// 创建3列表格
Table table = new Table(3);
// 添加表头
table.addHeaderCell(new Cell().add(new Paragraph("姓名").setBold()));
table.addHeaderCell(new Cell().add(new Paragraph("部门").setBold()));
table.addHeaderCell(new Cell().add(new Paragraph("工资").setBold()));
// 添加数据行
table.addCell(new Cell().add(new Paragraph("张三")));
table.addCell(new Cell().add(new Paragraph("技术部")));
table.addCell(new Cell().add(new Paragraph("8000")));
table.addCell(new Cell().add(new Paragraph("李四")));
table.addCell(new Cell().add(new Paragraph("市场部")));
table.addCell(new Cell().add(new Paragraph("7500")));
table.addCell(new Cell().add(new Paragraph("王五")));
table.addCell(new Cell().add(new Paragraph("财务部")));
table.addCell(new Cell().add(new Paragraph("8200")));
document.add(new Paragraph("员工信息表").setFontSize(16).setBold());
document.add(table);
document.close();
}
/**
* 生成带条形码的PDF
*/
public void createPdfWithBarcode(OutputStream outputStream, String barcodeData) throws IOException {
PdfWriter writer = new PdfWriter(outputStream);
PdfDocument pdfDoc = new PdfDocument(writer);
Document document = new Document(pdfDoc);
// 添加标题
document.add(new Paragraph("条形码示例文档")
.setFontSize(18)
.setBold()
.setTextAlignment(TextAlignment.CENTER));
// 添加条形码
document.add(new Paragraph("条形码数据: " + barcodeData));
// 创建Code 128条形码
Barcode128 barcode = new Barcode128(pdfDoc);
barcode.setCode(barcodeData);
barcode.setFont(null); // 不显示文本
// 将条形码转换为图像
Image barcodeImage = new Image(barcode.createFormXObject(pdfDoc))
vxMHRBD .setWidth(200)
.setAutoScaleHeight(true);
document.add(barcodeImage);
document.close();
}
/**
* 生成带图片和条形码的PDF
*/
public void createPdfWithImageAndBarcode(OutputStream outputStream, String imageUrl, String barcodeData) throws IOException {
PdfWriter writer = new PdfWriter(outputStream);
PdfDocument pdfDoc = new PdfDocument(writer);
Document document = new Document(pdfDoc);
// 添加标题
document.add(new Paragraph("产品信息卡")
.setFontSize(18)
.setBold()
.setTextAlignment(TextAlignment.CENTER));
// 添加图片(如果有)
if (imageUrl != null && !imageUrl.trim().isEmpty()) {
try {
Image image = new Image(ImageDataFactory.create(imageUrl))
.setWidth(200)
.setAutoScaleHeight(true)
.setHorizontalAlignment(HorizontalAlignment.CENTER);
document.add(image);
} catch (Exception e) {
document.add(new Paragraph("图片加载失败: " + e.getMessage()));
}
}
// 添加条形码
if (barcodeData != null && !barcodeData.trim().isEmpty()) {
Barcode128 barcode = new Barcode128(pdfDoc);
barcode.setCode(barcodeData);
Image barcodeImage = new Image(barcode.createFormXObject(pdfDoc))
.setWidth(250)
.setAutoScaleHeight(true)
.setHorizontalAlignment(HorizontalAlignment.CENTER);
document.add(new Paragraph("产品条形码:").setBold());
document.add(barcodeImage);
}
document.close();
}
/**
* 创建报告PDF
*/
public void createReportPdf(OutputStream outputStream) throws IOException {
PdfWriter writer = new PdfWriter(outputStream);
PdfDocument pdfDoc = new PdfDocument(writer);
Document document = new Document(pdfDoc);
// 报告标题
document.add(new Paragraph("月度销售报告")
.setFontSize(24)
.setBold()
.setTextAlignment(TextAlignment.CENTER));
// 报告信息
document.add(new Paragraph("报告周期: " + new Date()));
document.add(new Paragraph("生成部门: 销售部"));
// 销售数据表格
Table salesTable = new Table(4);
salesTable.addHeaderCell(new Cell().add(new Paragraph("产品").setBold()));
salesTable.addHeaderCell(new Cell().add(new Paragraph("季度").setBold()));
salesTable.addHeaderCell(new Cell().add(new Paragraph("销售额").setBold()));
salesTable.addHeaderCell(new Cell().add(new Paragraph("增长率").setBold()));
// 示例数据
String[][] salesData = {
{"产品A", "Q1", "120,000", "15%"},
{"产品A", "Q2", "138,000", "15%"},
{"产品B", "Q1", "85,000", "8%"},
{"产品B", "Q2", "92,000", "8%"}
};
for (String[] row : salesData) {
for (String cell : row) {
salesTable.addCell(new Cell().add(new Paragraph(cell)));
}
}
document.add(salesTable);
document.close();
}
/**
* 创建发票PDF
*/
public void createInvoicePdf(OutputStream outputStream) throws IOException {
PdfWriter writer = new PdfWriter(outputStream);
PdfDocument pdfDoc = new PdfDocument(writer);
Document document = new Document(pdfDoc);
// 发票标题
document.add(new Paragraph("商业发票")
.setFontSize(20)
.setBold()
.setTextAlignment(TextAlignment.CENTER));
// 发票信息表格
Table invoiceTable = new Table(2);
invoijavascriptceTable.addCell(new Cell().add(new Paragraph("发票号码:").setBold()));
invoiceTable.addCell(new Cell().add(new Paragraph("INV-2024-001")));
invoiceTable.addCell(new Cell().add(new Paragraph("开票日期:").setBold()));
invoiceTable.addCell(new Cell().add(new Paragraph(new Date().toString())));
invoiceTable.addCell(new Cell().add(new Paragraph("客户名称:").setBold()));
invoiceTable.addCell(new Cell().add(new Paragraph("某某科技有限公司")));
document.add(invoiceTable);
// 商品明细表格
Table itemsTable = new Table(4);
itemsTable.addHeaderCell(new Cell().add(new Paragraph("商品名称").setBold()));
itemsTable.addHeaderCell(new Cell().add(new Paragraph("数量").setBold()));
itemsTable.addHeaderCell(new Cell().add(new Paragraph("单价").setBold()));
itemsTable.addHeaderCell(new Cell().add(new Paragraph("金额").setBold()));
itemsTable.addCell(new Cell().add(new Paragraph("笔记本电脑")));
itemsTable.addCell(new Cell().add(new Paragraph("2")));
itemsTable.addCell(new Cell().add(new Paragraph("5,999")));
itemsTable.addCell(new Cell().add(new Paragraph("11,998")));
itemsTable.addCell(new Cell().add(new Paragraph("无线鼠标")));
itemsTable.addCell(new Cell().add(new Paragraph("5")));
itemsTable.addCell(new Cell().add(new Paragraph("89")));
itemsTable.addCell(new Cell().add(new Paragraph("445")));
document.add(new Paragraph("商品明细:").setBold());
document.add(itemsTable);
// 总计
document.add(new Paragraph("总计: 12,443").setBold().setFontSize(16));
document.close();
}
}
2. 中文PDF服务
@Service
public class ChinesePdfService {
/**
* 创建支持中文的PDF文档
*/
public void createChinesePdf(OutputStream outputStream) throws IOException {
PdfWriter writer = new PdfWriter(outputStream);
PdfDocument pdfDoc = new PdfDocument(writer);
Document document = new Document(pdfDoc);
// 使用中文字体
PdfFont chineseFont = PdfFontFactory.createFont("STSong-Light", "UniGB-UCS2-H", true);
// 添加中文内容
document.add(new Paragraph("中文PDF文档示例")
.setFont(chineseFont)
.setFontSize(20)
.setBold()
.setTextAlignment(TextAlignment.CENTER));
document.add(new Paragraph("企业名称:某某科技有限公司")
.setFont(chineseFont));
document.add(newjs Paragraph("公司地址:北京市海淀区中关村大街1号")
.setFont(chineseFont));
document.add(new Paragraph("联系电话:010-12345678")
.setFont(chineseFont));
// 中文表格
Table table = new Table(3);
table.addHeaderCell(new Cell().add(new Paragraph("姓名").setFont(chineseFont).setBold()));
table.addHeaderCell(new Cell().add(new Paragraph("职位").setFont(chineseFont).setBold()));
table.addHeaderCell(new Cell().add(new Paragraph("部门").setFont(chineseFont).setBold()));
table.addCell(new Cell().add(new Paragraph("张三").setFont(chineseFont)));
table.addCell(new Cell().add(new Paragraph("软件工程师").setFont(chineseFont)));
table.addCell(new Cell().add(new Paragraph("技术部").setFont(chineseFont)));
table.addCell(new Cell().add(new Paragraph("李四").setFont(chineseFont)));
table.addCell(new Cell().add(new Paragraph("产品经理").setFont(chineseFont)));
table.addCell(new Cell().add(new Paragraph("产品部").setFont(chineseFont)));
document.add(table);
document.close();
}
}
3. 模板PDF服务
@Service
public class TemplatePdfService {
/**
* 基于模板填充PDF表单
*/
public void fillPdfTemplate(OutputStream outputStream, Map<String, String> formData) throws IOException {
// 创建临时模板(实际项目中应该从文件系统或数据库读取模板)
byte[] templateBytes = createTemplatePdf();
// 读取模板
PdfReader reader = new PdfReader(new ByteArrayInputStream(templateBytes));
PdfWriter writer = new PdfWriter(outputStream);
PdfDocument pdfDoc = new PdfDocument(reader, writer);
// 获取表单
PdfAcroForm form = PdfAcroForm.getAcroForm(pdfDoc, true);
// 填充数据
for (Map.Entry<String, String> entry : formData.entrySet()) {
String fieldName = entry.getKey();
String fieldValue = entry.getValue();
PdfFormField field = form.getField(fieldName);
if (field != null) {
field.setValue(fieldValue);
}
}
// 扁平化表单(使字段不可编辑)
form.flattenFields();
pdfDoc.close();
}
/**
* 创建示例模板PDF(实际项目中应使用现有的PDF模板)
*/
private byte[] createTemplatePdf() throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PdfWriter writer = new PdfWriter(baos);
PdfDocument pdfDoc = new PdfDocument(writer);
// 创建表单
PdfAcroForm form = PdfAcroForm.getAcroForm(pdfDoc, true);
// 添加表单字段
Rectangle rect = new Rectangle(100, 700, 200, 20);
PdfTextFormField nameField = PdfTextFormField.createText(pdfDoc, rect, "name", "");
form.addField(nameField);
rect = new Rectangle(100, 650, 200, 20);
PdfTextFormField emailField = PdfTextFormField.createText(pdfDoc, rect, "email", "");
form.addField(emailField);
rect = new Rectangle(100, 600, 200, 20);
PdfTextFormField phoneField = PdfTextFormField.createText(pdfDoc, rect, "phone", "");
form.addField(phoneField);
// 添加标签
Document document = new Document(pdfDoc);
document.add(new Paragraph("姓名:").setFixedPosition(50, 700, 50));
document.add(new Paragraph("邮箱:").setFixedPosition(50, 650, 50));
document.add(new Paragraph("电话:").setFixedPosition(50, 600, 50));
document.close();
return baos.toByteArray();
}
}
四、完整的PDF生成Controller
@RestController
@RequestMapping("/api/pdf")
@CrossOrigin(origins = "*")
public class PdfGeneratorController {
@Autowired
private PdfService pdfService;
@Autowired
private ChinesePdfService chinesePdfService;
@Autowired
private TemplatePdfService templatePdfService;
/**
* 1. 基础PDF生成接口
* 生成包含简单文本的PDF文档
*/
@GetMapping("/basic")
public ResponseEntity<byte[]> generateBasicPdf() {
try {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
pdfService.createSimplePdf(outputStream);
return ResponseEntity.ok()
.contentType(MediaType.APPLICATION_PDF)
.header("Content-Disposition", "inline; filename=\"basic-document.pdf\"")
.body(outputStream.toByteArray());
} catch (IOException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(("PDF生成失败: " + e.getMessage()).getBytes());
}
}
/**
* 2. 中文PDF生成接口
* 演示中文字体处理和中文内容支持
*/
@GetMapping("/chinese")
public ResponseEntity<byte[]> generateChinesePdf() {
try {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
python chinesePdfService.createChinesePdf(outputStream);
return ResponseEntity.ok()
.contentType(MediaType.APPLICATION_PDF)
.header("Content-Disposition", "inline; filename=\"chinese-document.pdf\"")
.body(outputStream.toByteArray());
} catch (IOException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(("中文PDF生成失败: " + e.getMessage()).getBytes());
}
}
/**
* 3. 表格PDF生成接口
* 展示表格创建和数据展示功能
*/
@GetMapping("/table")
public ResponseEntity<byte[]> generateTablePdf() {
try {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
pdfService.createTablePdf(outputStream);
return ResponseEntity.ok()
.contentType(MediaType.APPLICATION_PDF)
.header("Content-Disposition", "inline; filename=\"table-document.pdf\"")
.body(outputStream.toByteArray());
} catch (IOException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(("表格PDF生成失败: " + e.getMessage()).getBytes());
}
}
/**
* 4. 条形码PDF生成接口
* 演示条形码生成功能(需要barcodes模块)
*/
@PostMapping("/barcode")
public ResponseEntity<byte[]> generateBarcodePdf(@RequestParam String barcodeData) {
try {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
pdfService.createPdfWithBarcode(outputStream, barcodeData);
return ResponseEntity.ok()
.contentType(MediaType.APPLICATION_PDF)
.header("Content-Disposition", "inline; filename=\"barcode-document.pdf\"")
.body(outputStream.toByteArray());
} catch (IOException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(("条形码PDF生成失败: " + e.getMessage()).getBytes());
}
}
/**
* 5. 图片PDF生成接口
* 展示图片和条形码的混合文档
*/
@PostMapping("/image-barcode")
public ResponseEntity<byte[]> generateImageBarcodePdf(
@RequestParam(required = false) String imageUrl,
@RequestParam String barcodeData) {
try {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
pdfService.createPdfWithImageAndBarcode(outputStream, imageUrl, barcodeData);
return ResponseEntity.ok()
.contentType(MediaType.APPLICATION_PDF)
.header("Content-Disposition", "inline; filename=\"image-barcode-document.pdf\"")
.body(outputStream.toByteArray());
} catch (IOException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(("图片条形码PDF生成失败: " + e.getMessage()).getBytes());
}
}
/**
* 6. 模板PDF生成接口
* 基于现有PDF模板填充数据
*/
@PostMapping("/template")
public ResponseEntity<byte[]> generateTemplatePdf(@RequestBody Map<String, String> formData) {
try {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
templatePdfService.fillPdfTemplate(outputStream, formData);
return ResponseEntity.ok()
.contentType(MediaType.APPLICATION_PDF)
.header("Content-Disposition", "inline; filename=\"template-document.pdf\"")
.body(outputStream.toByteArray());
} catch (IOException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(("模板PDF生成失败: " + e.getMessage()).getBytes());
}
}
/**
* 7. 报告PDF下载接口
*/
@GetMapping("/download/report")
public ResponseEntity<byte[]> downloadReportPdf() {
try {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
pdfService.createReportPdf(outputStream);
return ResponseEntity.ok()
.contentType(MediaType.APPLICATION_PDF)
.header("Content-Disposition", "attachment; filename=\"sales-report.pdf\"")
.body(outputStream.toByteArray());
} catch (IOException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(("报告PDF下载失败: " + e.getMessage()).getBytes());
}
}
/**
* 8. 发票PDF下载接口
*/
@GetMapping("/download/invoice")
public ResponseEntity<byte[]> downloadInvoicePdf() {
try {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
pdfService.createInvoicePdf(outputStream);
return ResponseEntity.ok()
.contentType(MediaType.APPLICATION_PDF)
.header("Content-Disposition", "attachment; filename=\"invoice.pdf\"")
.body(outputStream.toByteArray());
} catch (IOException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(("发票PDF下载失败: " + e.getMessage()).getBytes());
}
}
}
五、iText框架优劣分析
优势
- 功能全面:支持文本、表格、图片、条形码、表单等几乎所有PDF功能
- 性能优秀:处理大型文档时性能表现良好
- 标准兼容:完美支持PDF/A、PDF/UA等国际标准
- 文档完善:官方文档详细,社区活跃
- 企业级支持:提供商业许可证和技术支持
劣势
- 学习曲线:API较为复杂,新手需要时间适应
- 许可证限制:AGPL协议对商业使用有限制
- 内存占用:处理大型文档时内存消耗较高
- 配置复杂:模块化设计增加了依赖管理的复杂度
六、不同版本差异对比
| 特性 | iText 5.x | iText 7.x/9.x | 说明 |
|---|---|---|---|
| 架构设计 | 单体架构 | 模块化设计 | iText 9按功能拆分为多个jar |
| API设计 | 传统API | 现代化API | iText 9 API更清晰一致 |
| 条形码支持 | 内置核心 | 独立模块 | iText 9需要单独引入barcodes |
| 性能表现 | 一般 | 优化提升 | iText 9底层重构,性能更好 |
| 学习资源 | 丰富 | 相对较少 | iText 5教程更多,iText 9较新 |
| 维护状态 | 维护模式 | 积极开发 | iText 9是未来发展方向 |
七、版本选择建议
选择iText 5.x的情况
- 维护遗留项目
- 需要大量现有代码示例
- 项目对性能要求不高
- 快速原型开发
选择iText 7.x/9.x的情况
- 新项目开发
- 需要最佳性能
- 长期维护考虑
- 需要使用最新PDF标准特性
八、最佳实践总结
- 依赖管理:仔细选择所需模块,避免引入不必要的依赖
- 资源清理:使用try-with-resources确保PDF文档正确关闭
- 异常处理:妥善处理IO异常和iText特定异常
- 内存管理:对于大文档,考虑分块处理和流式输出
- 字体优化:预加载和复用字体对象提升性能
总结
通过本文的完整示例和详细分析,你应该能够在SpringBoot项目中顺利集成iText 9.4.0,并根据具体需求选择合适的版本和功能模块。
iText虽然有一定的学习成本,但其强大的功能和稳定性使其成为企业级PDF处理的优选方案。
iText官方文档:https://itextpdf.com/
这篇关于SpringBoot集成iText快速生成PDF教程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!