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> 表示遍历后台返回的resultItems2、下面判断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(XML Path Language)是一种用于在XML文档中定位和选择节点的语…...
字符串匹配-KMP算法
KMP算法,字符串匹配算法,给定一个主串S,和一个字串T,返回字串T与之S匹配的数组下标。 在学KMP算法之前,对于两个字符串,主串S,和字串T,我们根据暴力匹配,定义两个指针,i指…...
Java面向对象之UML类图
UML类图 表示 public 类型, - 表示 private 类型,#表示protected类型方法的写法:方法的类型(、-) 方法名(参数名: 参数类型):返回值类型...
【机器学习】西瓜书学习心得及课后习题参考答案—第4章决策树
这一章学起来较为简单,也比较好理解。 4.1基本流程——介绍了决策树的一个基本的流程。叶结点对应于决策结果,其他每个结点则对应于一个属性测试;每个结点包含的样本集合根据属性测试的结果被划分到子结点中;根结点包含样本全集&a…...
2023.8.2
2022河南萌新联赛第(三)场:河南大学\神奇数字.cpp //题意:给定三个正整数a b c,求x满足满足abc同余x的个数。 //这个考虑同余的性质,就是两个数的差去取模为0的数肯定是这两个数的同余数,。因此我们计算三个数两两之…...
windows运行窗口常用快捷键命令
winr打开运行窗口,然后输入快捷命令:(当然utools和win11搜索也挺好用的) cmd : 命令行窗口(命令提示符窗口、cmd窗口)regedit : 注册表mspaint : 画图工具services.msc : 本地服务设置(比如查看mysql服务是否启动成功)devmgmt.ms…...
HDFS的QJM方案
Quorum Journal Manager仲裁日志管理器 介绍主备切换,脑裂问题解决---ZKFailoverController(zkfc)主备切换,脑裂问题解决-- Fencing(隔离)机制主备数据状态同步问题解决 HA集群搭建集群基础环境准备HA集群规…...
安装win版本的neo4j(2023最新版本)
安装win版本的neo4j 写在最前面安装 win版本的neo4j1. 安装JDK2.下载配置环境变量(也可选择直接点击快捷方式,就可以不用配环境了)3. 启动neo4j 测试代码遇到的问题及解决(每次环境都太离谱了,各种问题)连接…...
ChatGPT结合知识图谱构建医疗问答应用 (二) - 构建问答流程
一、ChatGPT结合知识图谱 上篇文章对医疗数据集进行了整理,并写入了知识图谱中,本篇文章将结合 ChatGPT 构建基于知识图谱的问答应用。 下面是上篇文章的地址: 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:将数组转为字符串,以及字符串转为数组的方式。 问题2、将对象转为字符串,以及字符串转为对象的方法。 方法: 问题1: 将数组转为字符串可以使用 join() 方法。例如&…...
Eureka 学习笔记2:EurekaClient
版本 awsVersion ‘1.11.277’ EurekaClient 接口实现了 LookupService 接口,拥有唯一的实现类 DiscoveryClient 类。 LookupService 接口提供以下功能: 获取注册表根据应用名称获取应用根据实例 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实现延时队列
缓存队列延时向接口报工,并支持多实例部署。 引入依赖 <dependency><groupId>org.redisson</groupId><artifactId>redisson-spring-data</artifactId><version>3.17.4</version> </dependency> 注入RedisClient …...
无限遍历,Python实现在多维嵌套字典、列表、元组的JSON中获取数据
目录 背景 思路 新建两个函数A和B,函数 A处理字典数据,被调用后,判断传递的参数,如果参数为字典,则调用自身; 如果是列表或者元组,则调用列表处理函数B; 函数 B处理列表&#x…...
信息学奥赛一本通——1180:分数线划定
文章目录 题目【题目描述】【输入】【输出】【输入样例】【输出样例】【提示】 AC代码 题目 【题目描述】 世博会志愿者的选拔工作正在 A 市如火如荼的进行。为了选拔最合适的人才,A市对所有报名的选手进行了笔试,笔试分数达到面试分数线的选手方可进入…...
SpringApplication对象的构建及spring.factories的加载时机
构建SpringApplication对象源码: 1、调用启动类的main()方法,该方法中调用SpringApplication的run方法。 SpringBootApplication public class SpringbootdemoApplication {public static void main(String[] args) {SpringApplication.run(SpringbootdemoApplication.class, …...
基于传统检测算法hog+svm实现图像多分类
直接上效果图: 代码仓库和视频演示b站视频005期: 到此一游7758258的个人空间-到此一游7758258个人主页-哔哩哔哩视频 代码展示: 数据集在datasets文件夹下 运行01train.py即可训练 训练结束后会保存模型在本地 运行02pyqt.py会有一个可视化…...
slice() 方法,使用 concat() 方法, [...originalArray],find(filter),移出类名 removeAttr()
在JavaScript中,在 JavaScript 中,clone 不是一个原生的数组方法。但是你可以使用其他方法来实现克隆数组的功能。 以下是几种常见的克隆数组的方法: 使用 slice() 方法: const originalArray [1, 2, 3]; const clonedArray …...
微信小程序之bind和catch
这两个呢,都是绑定事件用的,具体使用有些小区别。 官方文档: 事件冒泡处理不同 bind:绑定的事件会向上冒泡,即触发当前组件的事件后,还会继续触发父组件的相同事件。例如,有一个子视图绑定了b…...
Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动
一、前言说明 在2011版本的gb28181协议中,拉取视频流只要求udp方式,从2016开始要求新增支持tcp被动和tcp主动两种方式,udp理论上会丢包的,所以实际使用过程可能会出现画面花屏的情况,而tcp肯定不丢包,起码…...
【CSS position 属性】static、relative、fixed、absolute 、sticky详细介绍,多层嵌套定位示例
文章目录 ★ position 的五种类型及基本用法 ★ 一、position 属性概述 二、position 的五种类型详解(初学者版) 1. static(默认值) 2. relative(相对定位) 3. absolute(绝对定位) 4. fixed(固定定位) 5. sticky(粘性定位) 三、定位元素的层级关系(z-i…...
postgresql|数据库|只读用户的创建和删除(备忘)
CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...
动态 Web 开发技术入门篇
一、HTTP 协议核心 1.1 HTTP 基础 协议全称 :HyperText Transfer Protocol(超文本传输协议) 默认端口 :HTTP 使用 80 端口,HTTPS 使用 443 端口。 请求方法 : GET :用于获取资源,…...
破解路内监管盲区:免布线低位视频桩重塑停车管理新标准
城市路内停车管理常因行道树遮挡、高位设备盲区等问题,导致车牌识别率低、逃费率高,传统模式在复杂路段束手无策。免布线低位视频桩凭借超低视角部署与智能算法,正成为破局关键。该设备安装于车位侧方0.5-0.7米高度,直接规避树枝遮…...
SpringAI实战:ChatModel智能对话全解
一、引言:Spring AI 与 Chat Model 的核心价值 🚀 在 Java 生态中集成大模型能力,Spring AI 提供了高效的解决方案 🤖。其中 Chat Model 作为核心交互组件,通过标准化接口简化了与大语言模型(LLM࿰…...
论文阅读:Matting by Generation
今天介绍一篇关于 matting 抠图的文章,抠图也算是计算机视觉里面非常经典的一个任务了。从早期的经典算法到如今的深度学习算法,已经有很多的工作和这个任务相关。这两年 diffusion 模型很火,大家又开始用 diffusion 模型做各种 CV 任务了&am…...
JDK 17 序列化是怎么回事
如何序列化?其实很简单,就是根据每个类型,用工厂类调用。逐个完成。 没什么漂亮的代码,只有有效、稳定的代码。 代码中调用toJson toJson 代码 mapper.writeValueAsString ObjectMapper DefaultSerializerProvider 一堆实…...
Java后端检查空条件查询
通过抛出运行异常:throw new RuntimeException("请输入查询条件!");BranchWarehouseServiceImpl.java // 查询试剂交易(入库/出库)记录Overridepublic List<BranchWarehouseTransactions> queryForReagent(Branch…...
