后端导出PDF文件(付前端实现代码)

本文详细介绍了如何在JAVA后端使用Freemarker模板和Flying Saucer库来导出PDF文件。首先,前端通过请求后端接口触发导出,后端则通过加载FTL模板并填充数据,然后利用HtmlToPdf方法将HTML转换为PDF。在HTML模板中,设置了一些样式和内容,并在后端代码中处理了中文支持。最后,测试表明该方法能成功生成PDF文件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

JAVA后端导出PDF文件


前言

效果图:

一、前端代码

// 直接用href请求后端地址即可
window.location.href = http://localhost:8989/exportPdf;

二、使用步骤

1. ftl模板

注意:贴出的接口代码,读取的ftl模板的路径是,src\main\resources\templates\free.ftl

<#-- 如果值为null/空,则设置为空值 -->
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Freemarker</title>
    <style type="text/css">
        #all {
            width: 600px;
        <#-- 使文档在pdf页面居中 --> margin: auto;
        }

        table {
            width: 100%;
            height: auto;
        <#-- @@提醒@@:此处必须指定字体,不然不识别中文  --> font-family: SimSun;
            text-align: center;
        <#-- table中单元格自动换行 --> table-layout: fixed;
            word-wrap: break-word;
        }

        tr td {
            padding: 2px;
            font-size: 10px;
            height: 20px;
        }

        tr th {
            padding: 2px;
            font-weight: bold;
            font-size: 10px;
            height: 20px;
        }
    </style>
</head>
<body>
<div id="all">
    <table border="1" cellspacing="0" cellpadding="0" align="center">
        <tr>
            <th colspan="5">${title}</th>
        </tr>
        <tr>
            <#-- 动态获取params的大小size -->
            <th rowspan="${params?size+1}">基本信息</th>
            <td>姓名</td>
            <td>电话</td>
            <td>昵称</td>
            <td>邮箱</td>
        </tr>
        <#list params as param>
            <tr>
                <td>${param.name}</td>
                <td>${param.phone}</td>
                <td>${param.nickname}</td>
                <td>${param.email}</td>
            </tr>
        </#list>
    </table>
</div>
</body>
</html>

2.后端代码

依赖:

 <dependency>
      <groupId>org.xhtmlrenderer</groupId>
      <artifactId>flying-saucer-pdf-itext5</artifactId>
      <version>9.0.3</version>
  </dependency>
  
  <dependency>
        <groupId>org.jsoup</groupId>
        <artifactId>jsoup</artifactId>
        <version>1.13.1</version>
  </dependency>
  
  <dependency>
      <groupId>org.freemarker</groupId>
      <artifactId>freemarker</artifactId>
      <version>2.3.30</version>
  </dependency>
      
  <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
  
 <dependency>
     <groupId>org.projectlombok</groupId>
     <artifactId>lombok</artifactId>
     <version>${lombok.version}</version>
 </dependency>
 
   <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-all</artifactId>
        <version>5.6.0</version>
    </dependency>

2.1 核心接口代码

package com.example.export;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import freemarker.template.Configuration;
import freemarker.template.Template;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 导出pdf
 *
 */
@Slf4j
@RestController
public class ExportPdf {


    @GetMapping("/exportPdf")
    public void exportPdf(HttpServletResponse response) {
        // ========== pdf处理方式 ===========
        Map<String, Object> map1 = new HashMap<>();
        map1.put("name", "赵小刚");
        map1.put("phone", "15673890987");
        map1.put("nickname", "红孩儿");
        map1.put("email", "15673890987@163.com");

        Map<String, Object> map2 = new HashMap<>();
        map2.put("name", "赵小刚");
        map2.put("phone", "15673890987");
        map2.put("nickname", "红孩儿");
        map2.put("email", "15673890987@163.com");
        List<Map<String, Object>> stuffList = Arrays.asList(map1,map2);

        Map<String, Object> stuffMap = new HashMap<>();
        stuffMap.put("params", stuffList);
        stuffMap.put("title", "测试PDF模板");


        File pdfFile = FileUtil.file("test.pdf");
        try (OutputStream os = new FileOutputStream(pdfFile);) {
            Configuration cfg = new Configuration(Configuration.VERSION_2_3_30);
            // 如果填充值中与模板不匹配,或者为null值,则默认此值为空;
            cfg.setClassicCompatible(true);
            // 模板路径加载方式:基于类路径,即在src\main\resources\templates包下的ftl模板
            cfg.setClassForTemplateLoading(this.getClass(),"/templates");
            Template template = cfg.getTemplate( "pdf.ftl");
            Writer writer = new StringWriter();
            template.process(stuffMap, writer);

            // 对pdf资料进行创建
            HtmlToPdf.htmlToPdf(writer.toString(),os);
            byte[] dataByte = FileUtil.readBytes(pdfFile);

            response.addHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(dataByte.length));
            response.setHeader(HttpHeaders.CONTENT_DISPOSITION, String.format("attachment; filename=%s"
                    , URLEncoder.encode("pdf测试.pdf", "UTF-8")));
            IoUtil.write(response.getOutputStream(), Boolean.TRUE, dataByte);

        } catch (Exception e) {
            log.error("导出贷后检查表失败, 详情:{}", e.getMessage());
        } finally {
            // 删除项目classes/目录下生成的临时文件
            if (pdfFile.exists()) {
                FileUtil.del(pdfFile);
            }
        }


    }

}

2.2 本地方法 HtmlToPdf.htmlToPdf()

package com.example.export;

import cn.hutool.core.util.StrUtil;
import com.itextpdf.text.pdf.BaseFont;
import lombok.extern.slf4j.Slf4j;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.xhtmlrenderer.pdf.ITextFontResolver;
import org.xhtmlrenderer.pdf.ITextRenderer;

import java.io.OutputStream;

 /**
     * 将HTML转成PD格式的文件。html文件的格式比较严格
     * @param htmlText  html内容
     * @param os   pdf存储文件输出流, 需要调用方关闭流
     * @throws Exception
     */
    public static void htmlToPdf(String htmlText, OutputStream os) throws Exception {
        // <!DOCTYPE html> 没有闭合标签,故删掉
        htmlText = htmlText.replace("<!DOCTYPE html>", StrUtil.EMPTY);
        Document document = Jsoup.parse(htmlText);
        /*手动为一些标签添加闭合*/
        Elements meta = document.getElementsByTag("meta");
        String html = document.html();
        for (Element link : meta) {
            String s = link.outerHtml();
            String s1 = s.replace("/>", ">");
            html = html.replace(s, s1 + "</meta>");
        }
        Elements link1 = document.getElementsByTag("link");
        for (Element link : link1) {
            String s = link.outerHtml();
            String s1 = s.replace("/>", ">");
            html = html.replace(s, s1 + "</link>");

        }
        html = html.replaceAll("<br>", "<br/>");
        html = html.replaceAll("&nbsp", "&#160");

        ITextRenderer renderer = new ITextRenderer();
        renderer.setDocumentFromString(html);
        // 解决中文支持, 注意本地和生产必须有此字体文件
        ITextFontResolver fontResolver = renderer.getFontResolver();
        String currentSystem = System.getProperty("os.name").toLowerCase();
        if("linux".equals(currentSystem)){
            fontResolver.addFont("/usr/share/fonts/chinese/simsun.ttc", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
        }else{
            fontResolver.addFont("C:/Windows/Fonts/simsun.ttc", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
        }

        renderer.layout();
        renderer.createPDF(os);
    }

3.测试

启动项目,使用浏览器请求:http://localhost:8989/exportPdf
即可实现导出pdf测试文件

(本文完!转载需说明出处!)

### 前端后端导出PDF的技术对比及实现方式 #### 前端实现PDF导出的方式 在前端实现PDF导出主要依赖于JavaScript库来处理HTML内容并将其转换为PDF格式。以下是两种常见方法: 1. **基于HTML内容的PDF生成** 使用 `html2canvas` 和 `jsPDF` 库可以轻松地将页面中的DOM元素渲染成图片,并进一步嵌入到PDF文档中。这种方法适用于简单的静态页面或特定区域的内容导出[^1]。 下面是一个具体的代码示例展示如何通过指定ID选择器捕获目标区块并转化为PDF文件: ```javascript import { jsPDF } from 'jspdf'; import html2canvas from 'html2canvas'; function GetPdfFromHtml(selector, fileName) { const element = document.getElementById(selector); html2canvas(element).then(canvas => { const imgData = canvas.toDataURL('image/png'); const pdf = new jsPDF(); const imgProps= pdf.getImageProperties(imgData); const pdfWidth = pdf.internal.pageSize.getWidth(); const pdfHeight = (imgProps.height * pdfWidth) / imgProps.width; pdf.addImage(imgData, 'PNG', 0, 0, pdfWidth, pdfHeight); pdf.save(`${fileName}.pdf`); }); } ``` 2. **直接构建PDF结构** 如果不需要保留原有网页样式,可以直接利用 `jsPDF` 创建自定义布局和字体样式的PDF文档。这种方式更加灵活但也更耗时[^2]。 #### 后端实现PDF导出的方式 相比之下,在服务器端生成PDF通常会采用更为强大的工具集和服务框架完成复杂的报表设计需求。例如Node.js环境下的 Puppeteer 或者 Java Spring Boot 中集成 iText 都是非常流行的选择之一。 - **Puppeteer**: 它允许开发者启动无头Chrome浏览器实例加载完整的Web应用界面后再截图保存为矢量图形形式(PDF),特别适合那些动态交互较多的应用场景[^3]。 示例代码如下所示: ```javascript const puppeteer = require('puppeteer'); async function generatePDF(url, path){ const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.goto(url,{waitUntil:'networkidle2'}); await page.pdf({path:path,format:'A4'}); await browser.close(); } // 调用函数 generatePDF('http://example.com','/tmp/example.pdf'); ``` - **iText7 for Java**: 提供了一套全面API用于创建高度定制化的PDF文档,支持多国语言文字排版以及高级安全特性如数字签名验证等操作[^4]。 #### 技术比较分析 从前述描述可以看出两者各有优劣之处: | 特性 | 前端 | 后端 | |--------------------|-------------------------|--------------------------| | 易用性和开发成本 | 较低 | 较高 | | 性能表现 | 对小型数据较好 | 处理大数据能力更强 | | 可扩展性 | 局限于当前显示效果 | 支持复杂业务逻辑处理 | | 用户体验影响 | 不占用服务资源 | 可能增加服务器负载压力 | 综上所述,具体选用哪种方案需视实际应用场景而定。如果只是简单的需求比如表格记录或者固定模板打印,则推荐优先考虑客户端解决方案;而对于涉及大量计算、外部接口调用或是需要严格控制输出质量的情况则更适合交给后台程序去执行。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_函数_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值