使用 Spring Boot 集成 Thymeleaf 和 Flying Saucer 实现 PDF 导出

在 Spring Boot 项目中,生成 PDF 报表或发票是常见需求。本文将介绍如何使用 Spring Boot 集成 Thymeleaf 模板引擎和 Flying Saucer 实现 PDF 导出,并提供详细的代码实现和常见问题解决方案。


目录

      • 一、项目依赖
      • [二、创建 Thymeleaf 模板](#二、创建 Thymeleaf 模板)
      • [三、创建 PDF 生成工具类](#三、创建 PDF 生成工具类)
      • [四、在 Spring Boot 中使用 PDF 生成功能](#四、在 Spring Boot 中使用 PDF 生成功能)
      • 五、常见错误及解决方案
        • [1. 中文字符乱码](#1. 中文字符乱码)
        • [2. 图片加载失败](#2. 图片加载失败)
        • [3. 样式不生效](#3. 样式不生效)
        • [4. 模板路径识别错误](#4. 模板路径识别错误)
        • [5. 依赖冲突](#5. 依赖冲突)
        • [6. 内存不足](#6. 内存不足)
      • 总结

一、项目依赖

要实现 PDF 导出功能,首先需要在项目中添加以下依赖:

xml 复制代码
<dependencies>
    <!-- Thymeleaf模板引擎 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>

    <!-- Flying Saucer for PDF generation -->
    <dependency>
        <groupId>org.xhtmlrenderer</groupId>
        <artifactId>flying-saucer-pdf</artifactId>
        <version>9.1.22</version>
    </dependency>

    <!-- iTextPDF (com.lowagie 版本,用于兼容 Flying Saucer) -->
    <dependency>
        <groupId>com.lowagie</groupId>
        <artifactId>itext</artifactId>
        <version>2.1.7</version>
    </dependency>
</dependencies>

注意 :Flying Saucer 使用的是旧版 iText 库 com.lowagie,而不是新版 com.itextpdf,确保使用正确版本以避免依赖冲突。


二、创建 Thymeleaf 模板

新建一个 HTML 模板用于生成发票内容,例如在 resources/templates/pdf/invoice.html 文件中编写如下 HTML:

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>发票</title>
    <style>
        body { font-family: Arial, sans-serif; margin: 20px; }
        h1 { text-align: center; }
        table { width: 100%; border-collapse: collapse; margin-top: 20px; }
        th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
        th { background-color: #f2f2f2; }
        .total { font-weight: bold; }
        .footer { margin-top: 30px; text-align: center; font-size: 12px; color: #888; }
    </style>
</head>
<body>
<h1>发票</h1>
<p>发票编号:<span th:text="${invoiceNumber}"></span></p>
<p>开具日期:<span th:text="${invoiceDate}"></span></p>
<p>买家姓名:<span th:text="${buyerName}"></span></p>

<h2>商品详情</h2>
<table>
    <thead>
    <tr>
        <th>商品名称</th>
        <th>描述</th>
        <th>数量</th>
        <th>单价</th>
        <th>小计</th>
    </tr>
    </thead>
    <tbody>
    <tr th:each="item : ${goodsItems}">
        <td th:text="${item.goodsName}"></td>
        <td th:text="${item.goodsDesc}"></td>
        <td th:text="${item.quantity}"></td>
        <td th:text="${item.unitPrice}"></td>
        <td th:text="${item.totalPrice}"></td>
    </tr>
    </tbody>
</table>

<p class="total">总金额:<span th:text="${finalAmount}"></span></p>
<div class="footer">感谢您的购买!</div>
</body>
</html>

三、创建 PDF 生成工具类

接下来,创建 PdfUtil 工具类,通过 Thymeleaf 渲染 HTML 并使用 Flying Saucer 将 HTML 转为 PDF:

java 复制代码
package com.xyh.transaction.utils;

import com.xyh.transaction.entity.dto.goods.GoodsInvoice;
import com.xyh.transaction.exception.BusinessException;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import org.xhtmlrenderer.pdf.ITextRenderer;

import java.io.ByteArrayOutputStream;
import java.util.List;

public class PdfUtil {

    public static byte[] generateInvoicePdf(String buyerName, String invoiceNumber,
                                            List<GoodsInvoice> goodsItems, String finalAmount) {
        TemplateEngine templateEngine = new TemplateEngine();

        // 使用 Thymeleaf 渲染 HTML
        Context context = new Context();
        context.setVariable("buyerName", buyerName);
        context.setVariable("invoiceNumber", invoiceNumber);
        context.setVariable("goodsItems", goodsItems);
        context.setVariable("finalAmount", finalAmount);
        String htmlContent = templateEngine.process("pdf/invoice.html", context);

        // 将 HTML 转换为 PDF
        try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
            ITextRenderer renderer = new ITextRenderer();
            renderer.getFontResolver().addFont("/path/to/your/font/simhei.ttf", true); // 防止中文乱码
            renderer.setDocumentFromString(htmlContent);
            renderer.layout();
            renderer.createPDF(outputStream);
            return outputStream.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
            throw new BusinessException("发票PDF 生成失败");
        }
    }
}

四、在 Spring Boot 中使用 PDF 生成功能

在 Spring Boot 控制器中调用 PdfUtil.generateInvoicePdf,将生成的 PDF 返回给用户或作为附件发送邮件:

java 复制代码
@RestController
@RequestMapping("/invoice")
public class InvoiceController {

    @GetMapping("/generate")
    public ResponseEntity<byte[]> generateInvoice() {
        byte[] pdfBytes = PdfUtil.generateInvoicePdf("张三", "INV-12345",
                goodsItems, "1000.00");
        
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_PDF);
        headers.setContentDispositionFormData("attachment", "invoice.pdf");
        
        return new ResponseEntity<>(pdfBytes, headers, HttpStatus.OK);
    }
}

五、常见错误及解决方案

1. 中文字符乱码
  • 问题:生成的 PDF 中,中文字符可能出现乱码。
  • 解决方案 :确保 addFont 引用支持中文的字体文件(如 simhei.ttf)。
2. 图片加载失败
  • 问题:HTML 中的图片在 PDF 中无法正常显示。

  • 解决方案 :将图片路径设置为绝对路径或通过 setBaseURL 指定资源根路径:

    java 复制代码
    renderer.getSharedContext().setBaseURL("file:/path/to/resources/");
3. 样式不生效
  • 问题:Flying Saucer 不支持高级 CSS。
  • 解决方案 :使用基本的 table 布局和简单 CSS,不依赖 flex、CSS 变量等。
4. 模板路径识别错误
  • 问题:模板引擎找不到指定 HTML 文件。
  • 解决方案 :检查文件路径和模板配置,确保模板放置在 resources/templates/pdf 目录下。
5. 依赖冲突
  • 问题:Flying Saucer 使用旧版 iText,与其他依赖产生冲突。
  • 解决方案 :独立模块管理依赖,使用 maven-shade-plugin 处理冲突类。
6. 内存不足
  • 问题:生成大型 PDF 时出现内存溢出。
  • 解决方案:增加 JVM 内存配置,或简化 HTML 结构降低内存占用。

总结

使用 Spring Boot 集成 Thymeleaf 和 Flying Saucer 实现 PDF 导出是生成发票、报告等文档的高效方式。通过以上实现步骤和常见问题解决方案,希望可以帮助您顺利在项目中集成此功能。

相关推荐
customer083 小时前
【开源免费】基于SpringBoot+Vue.JS体育馆管理系统(JAVA毕业设计)
java·vue.js·spring boot·后端·开源
Miketutu3 小时前
Spring MVC消息转换器
java·spring
小小虫码5 小时前
项目中用的网关Gateway及SpringCloud
spring·spring cloud·gateway
计算机-秋大田5 小时前
基于微信小程序的电子竞技信息交流平台设计与实现(LW+源码+讲解)
spring boot·后端·微信小程序·小程序·课程设计
customer088 小时前
【开源免费】基于SpringBoot+Vue.JS景区民宿预约系统(JAVA毕业设计)
java·vue.js·spring boot·后端·开源
带刺的坐椅10 小时前
无耳科技 Solon v3.0.7 发布(2025农历新年版)
java·spring·mvc·solon·aop
精通HelloWorld!13 小时前
使用HttpClient和HttpRequest发送HTTP请求
java·spring boot·网络协议·spring·http
LUCIAZZZ14 小时前
基于Docker以KRaft模式快速部署Kafka
java·运维·spring·docker·容器·kafka
拾忆,想起14 小时前
如何选择Spring AOP的动态代理?JDK与CGLIB的适用场景
spring boot·后端·spring·spring cloud·微服务
鱼骨不是鱼翅15 小时前
Spring Web MVC基础第一篇
前端·spring·mvc