当前位置: 首页 > news >正文

SpringBoot+Freemark根据html模板动态导出PDF

SpringBoot+Freemark根据html模板导出PDF

  • 1、引入maven
  • 2、两个工具类
    • 2.1 test.html模板
    • 2.2 test.html模板中的Freemark语法
  • 3、controller导出pdf

1、引入maven

导出pdf的一些必要jar包
		<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>com.itextpdf</groupId><artifactId>itextpdf</artifactId><version>5.5.13</version></dependency><dependency><groupId>com.itextpdf.tool</groupId><artifactId>xmlworker</artifactId><version>5.5.13</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-freemarker</artifactId><version>2.3.6.RELEASE</version></dependency><dependency><groupId>org.freemarker</groupId><artifactId>freemarker</artifactId><version>2.3.28</version></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.4.3</version></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-io</artifactId><version>1.3.2</version></dependency>

2、两个工具类

GeneratePDF.java

注意1:工具类中的变量templates 表示在resource建个templates目录, test.html表示生成pdf的模板,water.png表示pdf的水印,这里我没加水印,如果要加水印,只用把下面我注释的三行代码放开就行。
注意2: test.html和water.png都放在templates目录
package cn.yx.zg.utils.pdf;import cn.hutool.core.io.resource.ClassPathResource;
import com.itextpdf.text.*;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfGState;
import com.itextpdf.text.pdf.PdfPageEvent;
import com.itextpdf.text.pdf.PdfWriter;
import com.itextpdf.tool.xml.XMLWorkerHelper;import java.io.*;
import java.nio.charset.Charset;public class GeneratePDF {public final static String TEMPLATE_DIRECTORY_INSPECTION = "/templates/";public final static String TEMPLATE_NAME_INSPECTION = "test.html";public final static String TEMPLATE_WATER_INSPECTION = "water.png";/*** HTML 转 PDF** @param content html内容* @param outPath 输出pdf路径* @return 是否创建成功*/public static boolean html2Pdf(String content, String outPath) throws FileNotFoundException {return html2Pdf(content, new FileOutputStream(outPath));}public static boolean html2Pdf(String content, OutputStream out) {try {Document document = new Document(); //创建一个标准的A4纸文档PdfWriter writer = PdfWriter.getInstance(document, out);//书写器与ducument文档关联document.open();//打开文档
//            addWaterMark(writer);writer.setPageEvent(new PdfPageEvent() {@Overridepublic void onOpenDocument(PdfWriter pdfWriter, Document document) {}@Overridepublic void onStartPage(PdfWriter pdfWriter, Document document) {
//                    addWaterMark(pdfWriter);}@Overridepublic void onEndPage(PdfWriter pdfWriter, Document document) {}@Overridepublic void onCloseDocument(PdfWriter pdfWriter, Document document) {}@Overridepublic void onParagraph(PdfWriter pdfWriter, Document document, float v) {}@Overridepublic void onParagraphEnd(PdfWriter pdfWriter, Document document, float v) {}@Overridepublic void onChapter(PdfWriter pdfWriter, Document document, float v, Paragraph paragraph) {}@Overridepublic void onChapterEnd(PdfWriter pdfWriter, Document document, float v) {}@Overridepublic void onSection(PdfWriter pdfWriter, Document document, float v, int i, Paragraph paragraph) {}@Overridepublic void onSectionEnd(PdfWriter pdfWriter, Document document, float v) {}@Overridepublic void onGenericTag(PdfWriter pdfWriter, Document document, Rectangle rectangle, String s) {}});XMLWorkerHelper.getInstance().parseXHtml(writer, document, new ByteArrayInputStream(content.getBytes()), null, Charset.forName("UTF-8"));document.close();//关闭文档} catch (Exception e) {System.out.println("生成模板内容失败" + e.fillInStackTrace());return false;}return true;}/*** HTML 转 PDF** @param content html内容* @return PDF字节数组*/public static byte[] html2Pdf(String content) {ByteArrayOutputStream outputStream = null;try {Document document = new Document();outputStream = new ByteArrayOutputStream();PdfWriter writer = PdfWriter.getInstance(document, outputStream);document.open();XMLWorkerHelper.getInstance().parseXHtml(writer, document, new ByteArrayInputStream(content.getBytes()), null, Charset.forName("UTF-8"));document.close();} catch (Exception e) {System.out.println("------生成pdf失败-------");}return outputStream.toByteArray();}public static void addWaterMark(PdfWriter writer) {Image image = null;try {ClassPathResource resource = new ClassPathResource(TEMPLATE_DIRECTORY_INSPECTION + TEMPLATE_WATER_INSPECTION);image = Image.getInstance(resource.getUrl());// 加入水印PdfContentByte waterMar = writer.getDirectContentUnder();// 开始设置水印waterMar.beginText();// 设置水印透明度PdfGState gs = new PdfGState();// 设置笔触字体不透明度为0.4fgs.setStrokeOpacity(0.2f);gs.setFillOpacity(0.2f);// 设置等比缩放image.scalePercent(25);// 依照比例缩放// image.scaleAbsolute(200,100);//自定义大小image.setRotation(45f);// 设置透明度waterMar.setGState(gs);// 添加水印图片
//                waterMar.addImage(image);image.setAbsolutePosition(110f, 130f);//结束设置waterMar.endText();waterMar.stroke();try {waterMar.addImage(image);} catch (DocumentException e) {e.printStackTrace();} finally {waterMar = null;gs = null;}} catch (BadElementException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}}

GetHtmlContent.java

package cn.yx.zg.utils.pdf;import freemarker.cache.ClassTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;import java.io.StringWriter;
import java.io.Writer;
import java.util.Map;public class GetHtmlContent {/*** 获取模板内容** @param templateDirectory 模板文件夹* @param templateName      模板文件名* @param paramMap          模板参数* @return* @throws Exception*/public static String getTemplateContent(String templateDirectory, String templateName, Map<String, Object> paramMap) throws Exception {Configuration configuration = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);//不兼容配置try {configuration.setTemplateLoader(new ClassTemplateLoader(GetHtmlContent.class, templateDirectory));
//            configuration.setDirectoryForTemplateLoading(new File(templateDirectory));//加载模板} catch (Exception e) {e.printStackTrace();}Writer out = new StringWriter();Template template = configuration.getTemplate(templateName, "UTF-8");//缓存template.process(paramMap, out);out.flush();out.close();return out.toString();}}

2.1 test.html模板

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"/><title>测试表单</title><style>body {font-family: SimSun;}.f-s-1 {font-size: 12px;}.table-main {width: 100%;border-collapse: collapse;border-right: 1px solid rgba(0, 0, 0, 0.7);border-bottom: 1px solid rgba(0, 0, 0, 0.7);margin-bottom: 30px;}.table-main td {border-left: 1px solid rgba(0, 0, 0, 0.7);border-top: 1px solid rgba(0, 0, 0, 0.7);padding: 4px;}.th1 {font-weight: bold;background-color: #d7d7d7;}.th2 {background-color: #f1f1f1;}.text-center{text-align: center;}</style>
</head>
<body><h5 style="margin-top: 30px">学生列表</h5>
<table class="table-main f-s-1"><thead><tr><td class="th1" style="width: 28%">姓名</td><td class="th1 text-center" style="width: 12%">性别</td><td class="th1 text-center" style="width: 12%">年龄</td><td class="th1 text-center" style="width: 12%">地址</td></tr></thead><tbody><#list resultItems as item><tr><td class="text-center">${item.name}</td><td class="text-center">${item.sex}</td><td class="text-center">${item.age}</td><td class="text-center">${item.address}</td></tr></#list></tbody>
</table></body>
</html>

2.2 test.html模板中的Freemark语法

1、 <#list resultItems as item> 表示遍历后台返回的resultItems
2、下面判断age字段是否为空。
<#if age?? >存在<#else >不存在
</#if>
<#--判断数据是否存在,存在则则遍历-->
<#if resultItems??><#list resultItems as item>${item}</#list>
</#if>

3、controller导出pdf

注意: 代码中设置的变量要和html模板中的变量一致。
package cn.yx.zg.controller;import cn.yx.zg.utils.pdf.GeneratePDF;
import cn.yx.zg.utils.pdf.GetHtmlContent;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletResponse;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;@RestController
public class TestExportPdfController {@RequestMapping("/test")public void test(HttpServletResponse response) throws Exception {Map<String, Object> paramMap = new HashMap<>();List<Map<String, Object>> resultItems = new ArrayList<>();for (int i = 0; i < 100; i++) {Map<String, Object> items = new HashMap<>();items.put("name", "张三" + i);items.put("age", "18" + i);items.put("sex", "男" + i);items.put("address", "河南" + i);resultItems.add(items);}paramMap.put("resultItems", resultItems);String destFileName = "测试表单.pdf";destFileName = new String(destFileName.getBytes(), StandardCharsets.ISO_8859_1);OutputStream out;//设置响应response.setHeader("Content-Disposition", "attachment;filename=" + destFileName);response.setContentType("application/pdf");out = response.getOutputStream();String templateContent = GetHtmlContent.getTemplateContent(GeneratePDF.TEMPLATE_DIRECTORY_INSPECTION, GeneratePDF.TEMPLATE_NAME_INSPECTION, paramMap);GeneratePDF.html2Pdf(templateContent, out);out.flush();}
}

相关文章:

SpringBoot+Freemark根据html模板动态导出PDF

SpringBootFreemark根据html模板导出PDF 1、引入maven2、两个工具类2.1 test.html模板2.2 test.html模板中的Freemark语法 3、controller导出pdf 1、引入maven 导出pdf的一些必要jar包 <dependency><groupId>org.projectlombok</groupId><artifactId>…...

XPath数据提取与贴吧爬虫应用示例

XPath数据提取与贴吧爬虫应用示例 XpathXpath概述Xpath Helper插件 XPath语法基本语法查找特定节点选取未知节点选取若干路径 lxml模块使用说明使用示例 百度贴吧爬虫 Xpath Xpath概述 XPath&#xff08;XML Path Language&#xff09;是一种用于在XML文档中定位和选择节点的语…...

字符串匹配-KMP算法

KMP算法&#xff0c;字符串匹配算法&#xff0c;给定一个主串S&#xff0c;和一个字串T,返回字串T与之S匹配的数组下标。 在学KMP算法之前&#xff0c;对于两个字符串&#xff0c;主串S&#xff0c;和字串T&#xff0c;我们根据暴力匹配&#xff0c;定义两个指针&#xff0c;i指…...

Java面向对象之UML类图

UML类图 表示 public 类型&#xff0c; - 表示 private 类型&#xff0c;#表示protected类型方法的写法&#xff1a;方法的类型(、-) 方法名(参数名&#xff1a; 参数类型)&#xff1a;返回值类型...

【机器学习】西瓜书学习心得及课后习题参考答案—第4章决策树

这一章学起来较为简单&#xff0c;也比较好理解。 4.1基本流程——介绍了决策树的一个基本的流程。叶结点对应于决策结果&#xff0c;其他每个结点则对应于一个属性测试&#xff1b;每个结点包含的样本集合根据属性测试的结果被划分到子结点中&#xff1b;根结点包含样本全集&a…...

2023.8.2

2022河南萌新联赛第&#xff08;三&#xff09;场&#xff1a;河南大学\神奇数字.cpp //题意&#xff1a;给定三个正整数a b c,求x满足满足abc同余x的个数。 //这个考虑同余的性质&#xff0c;就是两个数的差去取模为0的数肯定是这两个数的同余数,。因此我们计算三个数两两之…...

windows运行窗口常用快捷键命令

winr打开运行窗口,然后输入快捷命令:&#xff08;当然utools和win11搜索也挺好用的&#xff09; cmd : 命令行窗口&#xff08;命令提示符窗口、cmd窗口&#xff09;regedit : 注册表mspaint : 画图工具services.msc : 本地服务设置(比如查看mysql服务是否启动成功)devmgmt.ms…...

HDFS的QJM方案

Quorum Journal Manager仲裁日志管理器 介绍主备切换&#xff0c;脑裂问题解决---ZKFailoverController&#xff08;zkfc&#xff09;主备切换&#xff0c;脑裂问题解决-- Fencing&#xff08;隔离&#xff09;机制主备数据状态同步问题解决 HA集群搭建集群基础环境准备HA集群规…...

安装win版本的neo4j(2023最新版本)

安装win版本的neo4j 写在最前面安装 win版本的neo4j1. 安装JDK2.下载配置环境变量&#xff08;也可选择直接点击快捷方式&#xff0c;就可以不用配环境了&#xff09;3. 启动neo4j 测试代码遇到的问题及解决&#xff08;每次环境都太离谱了&#xff0c;各种问题&#xff09;连接…...

ChatGPT结合知识图谱构建医疗问答应用 (二) - 构建问答流程

一、ChatGPT结合知识图谱 上篇文章对医疗数据集进行了整理&#xff0c;并写入了知识图谱中&#xff0c;本篇文章将结合 ChatGPT 构建基于知识图谱的问答应用。 下面是上篇文章的地址&#xff1a; ChatGPT结合知识图谱构建医疗问答应用 (一) - 构建知识图谱 这里实现问答的流程…...

聊天系统登录后端实现

定义返回的数据格式 # Restful API from flask import jsonifyclass HttpCode(object):# 响应正常ok 200# 没有登陆错误unloginerror 401# 没有权限错误permissionerror 403# 客户端参数错误paramserror 400# 服务器错误servererror 500def _restful_result(code, messa…...

Ajax笔记_01(知识点、包含代码和详细解析)

Ajax_01笔记 前置知识点 在JavaScript中 问题1&#xff1a;将数组转为字符串&#xff0c;以及字符串转为数组的方式。 问题2、将对象转为字符串&#xff0c;以及字符串转为对象的方法。 方法&#xff1a; 问题1&#xff1a; 将数组转为字符串可以使用 join() 方法。例如&…...

Eureka 学习笔记2:EurekaClient

版本 awsVersion ‘1.11.277’ EurekaClient 接口实现了 LookupService 接口&#xff0c;拥有唯一的实现类 DiscoveryClient 类。 LookupService 接口提供以下功能&#xff1a; 获取注册表根据应用名称获取应用根据实例 id 获取实例信息 public interface LookupService<…...

Spring引入并启用log4j日志框架-----Spring框架

<?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 http://ma…...

Redis实现延时队列

缓存队列延时向接口报工&#xff0c;并支持多实例部署。 引入依赖 <dependency><groupId>org.redisson</groupId><artifactId>redisson-spring-data</artifactId><version>3.17.4</version> </dependency> 注入RedisClient …...

无限遍历,Python实现在多维嵌套字典、列表、元组的JSON中获取数据

目录 背景 思路 新建两个函数A和B&#xff0c;函数 A处理字典数据&#xff0c;被调用后&#xff0c;判断传递的参数&#xff0c;如果参数为字典&#xff0c;则调用自身&#xff1b; 如果是列表或者元组&#xff0c;则调用列表处理函数B&#xff1b; 函数 B处理列表&#x…...

信息学奥赛一本通——1180:分数线划定

文章目录 题目【题目描述】【输入】【输出】【输入样例】【输出样例】【提示】 AC代码 题目 【题目描述】 世博会志愿者的选拔工作正在 A 市如火如荼的进行。为了选拔最合适的人才&#xff0c;A市对所有报名的选手进行了笔试&#xff0c;笔试分数达到面试分数线的选手方可进入…...

SpringApplication对象的构建及spring.factories的加载时机

构建SpringApplication对象源码: 1、调用启动类的main()方法,该方法中调用SpringApplication的run方法。 SpringBootApplication public class SpringbootdemoApplication {public static void main(String[] args) {SpringApplication.run(SpringbootdemoApplication.class, …...

基于传统检测算法hog+svm实现图像多分类

直接上效果图&#xff1a; 代码仓库和视频演示b站视频005期&#xff1a; 到此一游7758258的个人空间-到此一游7758258个人主页-哔哩哔哩视频 代码展示&#xff1a; 数据集在datasets文件夹下 运行01train.py即可训练 训练结束后会保存模型在本地 运行02pyqt.py会有一个可视化…...

slice() 方法,使用 concat() 方法, [...originalArray],find(filter),移出类名 removeAttr()

在JavaScript中&#xff0c;在 JavaScript 中&#xff0c;clone 不是一个原生的数组方法。但是你可以使用其他方法来实现克隆数组的功能。 以下是几种常见的克隆数组的方法&#xff1a; 使用 slice() 方法&#xff1a; const originalArray [1, 2, 3]; const clonedArray …...

Ubuntu系统下交叉编译openssl

一、参考资料 OpenSSL&&libcurl库的交叉编译 - hesetone - 博客园 二、准备工作 1. 编译环境 宿主机&#xff1a;Ubuntu 20.04.6 LTSHost&#xff1a;ARM32位交叉编译器&#xff1a;arm-linux-gnueabihf-gcc-11.1.0 2. 设置交叉编译工具链 在交叉编译之前&#x…...

从零实现富文本编辑器#5-编辑器选区模型的状态结构表达

先前我们总结了浏览器选区模型的交互策略&#xff0c;并且实现了基本的选区操作&#xff0c;还调研了自绘选区的实现。那么相对的&#xff0c;我们还需要设计编辑器的选区表达&#xff0c;也可以称为模型选区。编辑器中应用变更时的操作范围&#xff0c;就是以模型选区为基准来…...

Opencv中的addweighted函数

一.addweighted函数作用 addweighted&#xff08;&#xff09;是OpenCV库中用于图像处理的函数&#xff0c;主要功能是将两个输入图像&#xff08;尺寸和类型相同&#xff09;按照指定的权重进行加权叠加&#xff08;图像融合&#xff09;&#xff0c;并添加一个标量值&#x…...

全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比

目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec&#xff1f; IPsec VPN 5.1 IPsec传输模式&#xff08;Transport Mode&#xff09; 5.2 IPsec隧道模式&#xff08;Tunne…...

浪潮交换机配置track检测实现高速公路收费网络主备切换NQA

浪潮交换机track配置 项目背景高速网络拓扑网络情况分析通信线路收费网络路由 收费汇聚交换机相应配置收费汇聚track配置 项目背景 在实施省内一条高速公路时遇到的需求&#xff0c;本次涉及的主要是收费汇聚交换机的配置&#xff0c;浪潮网络设备在高速项目很少&#xff0c;通…...

GitHub 趋势日报 (2025年06月06日)

&#x1f4ca; 由 TrendForge 系统生成 | &#x1f310; https://trendforge.devlive.org/ &#x1f310; 本日报中的项目描述已自动翻译为中文 &#x1f4c8; 今日获星趋势图 今日获星趋势图 590 cognee 551 onlook 399 project-based-learning 348 build-your-own-x 320 ne…...

PostgreSQL——环境搭建

一、Linux # 安装 PostgreSQL 15 仓库 sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-$(rpm -E %{rhel})-x86_64/pgdg-redhat-repo-latest.noarch.rpm# 安装之前先确认是否已经存在PostgreSQL rpm -qa | grep postgres# 如果存在&#xff0…...

给网站添加live2d看板娘

给网站添加live2d看板娘 参考文献&#xff1a; stevenjoezhang/live2d-widget: 把萌萌哒的看板娘抱回家 (ノ≧∇≦)ノ | Live2D widget for web platformEikanya/Live2d-model: Live2d model collectionzenghongtu/live2d-model-assets 前言 网站环境如下&#xff0c;文章也主…...

华为OD最新机试真题-数组组成的最小数字-OD统一考试(B卷)

题目描述 给定一个整型数组,请从该数组中选择3个元素 组成最小数字并输出 (如果数组长度小于3,则选择数组中所有元素来组成最小数字)。 输入描述 行用半角逗号分割的字符串记录的整型数组,0<数组长度<= 100,0<整数的取值范围<= 10000。 输出描述 由3个元素组成…...

机器学习的数学基础:线性模型

线性模型 线性模型的基本形式为&#xff1a; f ( x ) ω T x b f\left(\boldsymbol{x}\right)\boldsymbol{\omega}^\text{T}\boldsymbol{x}b f(x)ωTxb 回归问题 利用最小二乘法&#xff0c;得到 ω \boldsymbol{\omega} ω和 b b b的参数估计$ \boldsymbol{\hat{\omega}}…...