完美!SpringBoot3.3 + OpenPDF + HTML模板高效生成PDF文档

原创 编程疏影 路条编程 2024年08月30日 07:30 河北

图片

完美!SpringBoot3.3 + OpenPDF + Flying Saucer + HTML模板高效生成PDF文档

在现代Web开发中,生成PDF文档是一个常见的需求,无论是生成报表、导出合同还是制作发票,都需要高效、灵活的PDF生成方案。本篇文章将深入探讨如何使用 SpringBoot 3.3 结合 Thymeleaf、JavaScript 和 Bootstrap 实现高效的PDF文档生成,并通过详细的代码示例和配置说明,帮助大家快速上手这一技术方案。

随着业务需求的不断增长,传统的PDF生成方式已经难以满足复杂、多样化的需求。使用HTML模板生成PDF具有以下优势:

  • 可视化强:可以直接在浏览器中预览效果,所见即所得。

  • 样式灵活:借助CSS和Bootstrap,可以轻松实现复杂的布局和样式。

  • 开发高效:前后端分离,开发维护更加便捷。

本篇文章将基于 SpringBoot 3.3,利用 Thymeleaf 模板引擎和 OpenPDF 库,将HTML模板渲染为PDF文档,实现高效、灵活的PDF生成方案。

运行效果:

图片

生成 pdf 效果:

图片

若想获取项目完整代码以及其他文章的项目源码,且在代码编写时遇到问题需要咨询交流,欢迎加入下方的知识星球。

项目环境准备

1. 开发环境

  • JDK 17:确保您的项目运行在Java 17环境下。

  • Maven 3.8+:用于项目构建和依赖管理。

  • SpringBoot 3.3:最新版本的SpringBoot框架。

  • Thymeleaf:流行的Java模板引擎。

  • OpenPDF:开源的PDF生成库。

  • Bootstrap 5:用于前端页面的布局和样式。

  • JavaScript:用于增强页面交互性。

2. 项目结构

项目的基本结构如下:

pdf-demo
├── src
│   ├── main
│   │   ├── java
│   │   │   └── com.icoderoad.pdfdemo
│   │   │       ├── controller
│   │   │       ├── service
│   │   │       └── PdfDemoApplication.java
│   │   ├── resources
│   │   │   ├── templates
│   │   │   ├── fonts/SimSun.ttf
│   │   │   ├── static
│   │   │   └── application.yml
├── pom.xml

项目依赖配置(pom.xml)

首先,我们需要在 pom.xml 中添加必要的依赖。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>3.3.3</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.icoderoad</groupId>
	<artifactId>pdfdemo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>pdfdemo</name>
	<description>Demo project for Spring Boot</description>
	
	<properties>
		<java.version>17</java.version>
		<openpdf.version>1.3.29</openpdf.version>
	</properties>
	<dependencies>
		<!-- Spring Boot Starter Web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- Thymeleaf 模板引擎 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

        <!-- OpenPDF 库 -->
        <dependency>
            <groupId>com.github.librepdf</groupId>
            <artifactId>openpdf</artifactId>
            <version>${openpdf.version}</version>
        </dependency>

        <!-- HTML转PDF工具(flying-saucer) -->
        <dependency>
            <groupId>org.xhtmlrenderer</groupId>
            <artifactId>flying-saucer-core</artifactId>
            <version>9.1.20</version>
        </dependency>
        <dependency>
            <groupId>org.xhtmlrenderer</groupId>
            <artifactId>flying-saucer-pdf-openpdf</artifactId>
            <version>9.1.20</version>
        </dependency>

        <!-- 日志框架 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-logging</artifactId>
        </dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

依赖说明:

  • spring-boot-starter-web:提供Web开发所需的基本依赖。

  • spring-boot-starter-thymeleaf:集成Thymeleaf模板引擎。

  • openpdf:用于生成PDF文档的核心库。

  • flying-saucer:将HTML转换为PDF的工具,结合OpenPDF使用。

  • spring-boot-starter-logging:提供日志支持。

应用配置(application.yml)

在 src/main/resources 目录下创建 application.yml 配置文件。

# application.yml
server:
  port: 8080

spring:
  thymeleaf:
    prefix: classpath:/templates/
    suffix: .html
    mode: HTML
    encoding: UTF-8
    cache: false

配置说明:

  • server.port:设置应用运行端口为8080。

  • spring.thymeleaf:配置Thymeleaf模板引擎的相关属性,指定模板路径、后缀、编码等。

后端代码实现

1. 主启动类

创建主启动类 PdfDemoApplication.java

package com.icoderoad.pdfdemo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class PdfdemoApplication {

	public static void main(String[] args) {
		SpringApplication.run(PdfdemoApplication.class, args);
	}

}

2. 控制器层

创建控制器类 PdfController.java,负责处理请求和生成PDF文档。

// PdfController.java
package com.icoderoad.pdfdemo.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

import com.icoderoad.pdfdemo.service.PdfService;

import jakarta.servlet.http.HttpServletResponse;

@Controller
public class PdfController {

    @Autowired
    private PdfService pdfService;

    /**
     * 显示数据预览页面
     * @param model 模型数据
     * @return 模板名称
     */
    @GetMapping("/preview")
    public String preview(Model model) {
        model.addAttribute("title", "PDF文档预览");
        model.addAttribute("content", "这是一个使用Thymeleaf渲染的HTML内容,将被转换为PDF文档。");
        return "preview";
    }

    /**
     * 生成PDF文档并下载
     * @param response HTTP响应
     * @throws Exception 异常
     */
    @GetMapping("/download")
    public void downloadPdf(HttpServletResponse response) throws Exception {
        String htmlContent = pdfService.generateHtmlContent();
        pdfService.generatePdf(response, htmlContent);
    }
}

代码说明:

  • /preview:用于展示HTML内容的预览页面,方便在生成PDF前查看效果。

  • /download:生成PDF文档并通过HTTP响应下载。

3. 服务层

创建服务类 PdfService.java,负责处理HTML内容的生成和PDF文档的生成。

package com.icoderoad.pdfdemo.service;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import org.xhtmlrenderer.pdf.ITextFontResolver;
import org.xhtmlrenderer.pdf.ITextRenderer;

import com.lowagie.text.DocumentException;

import jakarta.servlet.http.HttpServletResponse;

@Service
public class PdfService {

    @Autowired
    private TemplateEngine templateEngine;

    /**
     * 生成HTML内容
     * @return 渲染后的HTML字符串
     */
    public String generateHtmlContent() {
        Context context = new Context();
        context.setVariable("title", "PDF文档标题");
        context.setVariable("content", 
        		"这是PDF文档的主要内容:"
        		+ "“成功并不是终点,失败也不是终结,最重要的是继续前行的勇气。在人生的旅途中,我们会遇到许多挑战与挫折,这些都是成长的必经之路。每一次跌倒都是一次学习的机会,每一次失败都为成功铺设了基础。只要我们保持信念,不断努力,最终会到达梦想的彼岸。无论前方的路有多么坎坷,只要心怀希望,我们就有无限的可能性去改变自己的命运,实现心中的理想。”"
        		+ "由Thymeleaf模板引擎渲染。"
        		+ "");
        return templateEngine.process("pdf_template", context);
    }

    /**
     * 将HTML内容转换为PDF并写入响应
     * @param response HTTP响应
     * @param htmlContent HTML内容
     * @throws IOException IO异常
     * @throws DocumentException 文档异常
     */
    public void generatePdf(HttpServletResponse response, String htmlContent) throws IOException, DocumentException {
        // 设置响应类型
        response.setContentType("application/pdf");
        response.setHeader("Content-Disposition", "attachment; filename=generated.pdf");

        // 创建ITextRenderer实例
        ITextRenderer renderer = new ITextRenderer();
       
        // 设置字体路径,使用 classpath 加载字体
        ITextFontResolver fontResolver = renderer.getFontResolver();
        ClassPathResource fontResource = new ClassPathResource("fonts/SimSun.ttf");
        System.out.println(fontResource.getFile().getAbsolutePath());
        fontResolver.addFont(fontResource.getFile().getAbsolutePath(), "Identity-H", true);
        
        renderer.setDocumentFromString(htmlContent);
        renderer.layout();

        // 输出PDF到响应输出流
        try (OutputStream outputStream = response.getOutputStream()) {
            renderer.createPDF(outputStream);
            outputStream.flush();
        }
    }
}

代码说明:

  • generateHtmlContent():使用Thymeleaf模板引擎渲染HTML内容,填充模板变量。

  • generatePdf():使用 ITextRenderer 将HTML内容转换为PDF文档,并写入HTTP响应,供用户下载。

注意事项:

  • 模板引擎配置:TemplateEngine 需要在配置类中进行相应设置,这里通过自动装配注入。

  • 字符编码:确保HTML内容的字符编码为UTF-8,避免中文乱码。

前端代码实现

1. Thymeleaf模板

在 src/main/resources/templates 目录下创建 preview.html 和 pdf_template.html 两个模板文件。

1.1 preview.html

用于在浏览器中预览内容。

<!-- preview.html -->
<!DOCTYPE html>
<html lang="zh-CN" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title th:text="${title}">PDF预览</title>
    <!-- 引入Bootstrap CSS -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
</head>
<body>
    <div class="container mt-5">
        <h1 class="text-center" th:text="${title}">PDF文档预览</h1>
        <p class="mt-4" th:text="${content}">这里是内容部分。</p>
        <div class="text-center mt-5">
            <a href="/pdf-demo/download" class="btn btn-primary">下载PDF文档</a>
        </div>
    </div>
    <!-- 引入Bootstrap JS -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

代码说明:

  • 使用 Bootstrap 5 进行简单的页面布局和样式设计。

  • 通过Thymeleaf的 th:text 属性渲染动态内容。

  • 提供一个下载PDF的按钮,链接到 /download`。

1.2 pdf_template.html

用于生成PDF的模板。

<!DOCTYPE html>
<html lang="zh-CN" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8"/>
    <title th:text="${title}">PDF文档</title>
    <style>
        /* 内联CSS样式,确保在PDF中正确渲染 */
        body {
            font-family: "SimSun", serif;
            padding: 20px;
            line-height: 1.6;
        }
        h1 {
            text-align: center;
            margin-bottom: 40px;
            color: #333;
        }
        p {
            font-size: 16px;
            color: #555;
        }
        .footer {
            text-align: center;
            margin-top: 50px;
            font-size: 12px;
            color: #999;
        }
    </style>
</head>
<body>
    <h1 th:text="${title}">PDF文档标题</h1>
    <p th:text="${content}">这是PDF文档的内容部分,由Thymeleaf模板引擎渲染。</p>
    <div class="footer">
        <p>© 2024 路条编程 - 保留所有权利。</p>
    </div>
</body>
</html>

代码说明:

  • 内联样式:为了确保PDF中样式正确渲染,使用内联CSS样式。

  • 字体设置:指定 font-family 为中文字体(如 "SimSun"),避免中文显示乱码。

  • 内容布局:简单的标题、内容和页脚布局,满足基本的PDF文档需求。

2. 静态资源

对于简单的示例,我们直接使用CDN引入Bootstrap的CSS和JS。如果需要自定义样式和脚本,可以在 src/main/resources/static 目录下添加相应的文件。

运行与测试

1. 启动应用

在项目根目录下执行以下命令启动应用:

mvn spring-boot:run

2. 访问预览页面

在浏览器中访问:

http://localhost:8080/preview

您将看到预览页面,展示了将要生成的PDF内容。

3. 下载PDF文档

在预览页面点击“下载PDF文档”按钮,浏览器将自动下载生成的PDF文件 generated.pdf

效果展示:

生成的PDF文档应当包含正确的中文内容和样式,确保没有乱码和样式错乱。

深度解析

1. 为什么选择OpenPDF和Flying Saucer

OpenPDF 是一个功能强大且稳定的开源PDF库,支持丰富的PDF操作。而 Flying Saucer 是一个专门用于将XHTML/CSS转换为PDF的库,与OpenPDF兼容性良好。两者结合,可以方便地将HTML模板转换为高质量的PDF文档。

优势:

  • 易于使用:API简洁明了,集成方便。

  • 支持CSS样式:能够很好地渲染复杂的CSS样式,满足多样化的布局需求。

  • 开源免费:无版权限制,适用于商业项目。

2. 字体和编码问题

在生成包含中文内容的PDF时,常常会遇到乱码或字体缺失的问题。为了解决这些问题,我们需要:

  • 指定中文字体:在CSS中设置 font-family 为系统支持的中文字体,如 "SimSun""Microsoft YaHei" 等。

  • 嵌入字体:确保PDF中嵌入所需的字体文件,可以通过配置ITextRenderer来实现。

  • 设置编码:确保HTML内容和生成的PDF使用统一的UTF-8编码。

示例:嵌入字体

renderer.getFontResolver().addFont("path/to/simsun.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);

注意:需要将字体文件放在项目的资源目录中,并指定正确的路径。

3. 处理复杂布局和样式

对于复杂的PDF文档,可以在HTML模板中使用更丰富的Bootstrap组件和自定义CSS,实现多栏布局、表格、图片等元素。同时,Thymeleaf的强大模板功能也可以帮助我们动态生成复杂的内容。

示例:在模板中使用表格

<table class="table table-bordered">
    <thead>
        <tr>
            <th>序号</th>
            <th>名称</th>
            <th>描述</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>1</td>
            <td>示例项</td>
            <td>这是一个示例描述。</td>
        </tr>
        <!-- 更多行 -->
    </tbody>
</table>

4. 动态数据填充

通过在服务层获取数据库或其他数据源的数据,并将其传递给Thymeleaf模板,我们可以动态生成包含业务数据的PDF文档。

示例:传递数据列表

List<Item> items = itemService.getItems();
context.setVariable("items", items);

在模板中迭代显示数据:

<tbody>
    <tr th:each="item, iterStat : ${items}">
        <td th:text="${iterStat.index + 1}">1</td>
        <td th:text="${item.name}">示例项</td>
        <td th:text="${item.description}">这是一个示例描述。</td>
    </tr>
</tbody>

总结

通过本文的讲解,我们成功实现了使用 SpringBoot 3.3 结合 Thymeleaf 模板引擎、高效生成PDF文档的功能。从环境配置、依赖管理到代码实现,再到样式设计和复杂布局处理,都进行了详细的阐述。

优势总结:

  • 高效性:利用现有的HTML模板和样式,快速生成所见即所得的PDF文档。

  • 灵活性:通过模板和数据的结合,轻松实现动态内容的生成。

  • 可维护性:清晰的前后端分离,方便维护和扩展。

未来展望:

  • 多语言支持:通过国际化配置,实现PDF文档的多语言版本。

  • 更复杂的样式:引入更丰富的前端框架,如Tailwind CSS,提升文档的美观度。

  • 性能优化:对于大批量PDF生成的场景,可以考虑异步处理和缓存机制,提升性能。

希望本文能对大家的开发工作有所帮助,助大家在项目中高效地实现PDF文档生成功能。

今天就讲到这里,如果有问题需要咨询,大家可以直接留言或扫下方二维码来知识星球找我,我们会尽力为你解答。

图片

在Spring Boot和Vue项目中实现PDF在线预览可以通过集成pdf.js库来实现。首先,您需要从官网下载pdf.js源码,并将其放入您的项目中。具体步骤如下: 1. 在官方网站下载pdf.js源码。 2. 将下载的源码放入您的Spring Boot项目中的某个目录中。 3. 打开viewer.html文件,您会注意到它会自动跳转到一个pdf文件。这个pdf文件是官方的示例文件。 4. 如果您只想打开一个特定的pdf文件,只需将官方示例文件替换为您自己的pdf文件。 5. 在viewer.js文件中搜索"defaultUrl",大约在第3500行左右。您可以找到类似上述代码的部分(注意:不同版本的pdf.js可能会略有差异)。 6. 只需更改"value"的值,即可实现预览您指定的pdf文件。 7. 如果您需要根据前端传递的不同值来响应不同的pdf文件,可以使用动态获取的方式来实现。 另外,如果希望使用浏览器自带的预览pdf功能,可以参考使用pdf.js的实现方式。但需要注意的是,某些浏览器可能不支持此功能,如360浏览器。您可以在我的博客中找到有关使用浏览器预览pdf的更多信息。 综上所述,您可以根据以上步骤在Spring Boot和Vue项目中实现PDF在线预览。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [springboot+vue整合pdf.js实现预览pdf](https://blog.csdn.net/qq_14853853/article/details/111176085)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值