当前位置: 首页 > 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 …...

浅谈 React Hooks

React Hooks 是 React 16.8 引入的一组 API&#xff0c;用于在函数组件中使用 state 和其他 React 特性&#xff08;例如生命周期方法、context 等&#xff09;。Hooks 通过简洁的函数接口&#xff0c;解决了状态与 UI 的高度解耦&#xff0c;通过函数式编程范式实现更灵活 Rea…...

19c补丁后oracle属主变化,导致不能识别磁盘组

补丁后服务器重启&#xff0c;数据库再次无法启动 ORA01017: invalid username/password; logon denied Oracle 19c 在打上 19.23 或以上补丁版本后&#xff0c;存在与用户组权限相关的问题。具体表现为&#xff0c;Oracle 实例的运行用户&#xff08;oracle&#xff09;和集…...

【网络】每天掌握一个Linux命令 - iftop

在Linux系统中&#xff0c;iftop是网络管理的得力助手&#xff0c;能实时监控网络流量、连接情况等&#xff0c;帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...

【Oracle APEX开发小技巧12】

有如下需求&#xff1a; 有一个问题反馈页面&#xff0c;要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据&#xff0c;方便管理员及时处理反馈。 我的方法&#xff1a;直接将逻辑写在SQL中&#xff0c;这样可以直接在页面展示 完整代码&#xff1a; SELECTSF.FE…...

.Net框架,除了EF还有很多很多......

文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...

连锁超市冷库节能解决方案:如何实现超市降本增效

在连锁超市冷库运营中&#xff0c;高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术&#xff0c;实现年省电费15%-60%&#xff0c;且不改动原有装备、安装快捷、…...

STM32标准库-DMA直接存储器存取

文章目录 一、DMA1.1简介1.2存储器映像1.3DMA框图1.4DMA基本结构1.5DMA请求1.6数据宽度与对齐1.7数据转运DMA1.8ADC扫描模式DMA 二、数据转运DMA2.1接线图2.2代码2.3相关API 一、DMA 1.1简介 DMA&#xff08;Direct Memory Access&#xff09;直接存储器存取 DMA可以提供外设…...

2025盘古石杯决赛【手机取证】

前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来&#xff0c;实在找不到&#xff0c;希望有大佬教一下我。 还有就会议时间&#xff0c;我感觉不是图片时间&#xff0c;因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...

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

&#x1f4ca; 由 TrendForge 系统生成 | &#x1f310; https://trendforge.devlive.org/ &#x1f310; 本日报中的项目描述已自动翻译为中文 &#x1f4c8; 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...

BCS 2025|百度副总裁陈洋:智能体在安全领域的应用实践

6月5日&#xff0c;2025全球数字经济大会数字安全主论坛暨北京网络安全大会在国家会议中心隆重开幕。百度副总裁陈洋受邀出席&#xff0c;并作《智能体在安全领域的应用实践》主题演讲&#xff0c;分享了在智能体在安全领域的突破性实践。他指出&#xff0c;百度通过将安全能力…...