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

springboot之HTML与图片生成

背景

后台需要根据字段动态生成HTML,并生成图片,发送邮件到给定邮箱

依赖

 <!-- freemarker模板引擎-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-freemarker</artifactId><version>2.7.17</version>
</dependency>
<!-- 图片生成 -->
<dependency><groupId>org.xhtmlrenderer</groupId><artifactId>core-renderer</artifactId><version>R8</version>
</dependency>

HTML模版 (ftl格式模板)

<!-- demo.ftl -->
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8" /><title>demo Receipt</title><style>body {font-family: Arial, sans-serif;background-color: #f0f0f0;margin: 20px;}hr {border: none;border-bottom:1px dashed black;}.receipt {background-color: #fff;padding: 20px;border-radius: 8px;box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);width: 320px;text-align: center;margin: 0 auto;}.fieldLabel, .fieldMiddle, .fieldValue {margin: 0 auto; /* 使子元素水平居中 */}.fieldLabel {font-size: 14px;/*font-weight: bold;*/text-align: left;}.fieldMiddle {font-size: 14px;/*font-weight: bold;*/text-align: left;}.fieldValue {font-size: 14px;text-align: right;}</style>
</head>
<body>
<!-- <div style="text-align: center; padding: 20px"> -->
<div class="receipt"><table>
<#--        content1--><#if content1??><tr><!-- 水平实线 --><td colspan="10"><hr/></td></tr><#list content1 as item><tr><td colspan="4" class="fieldLabel">${item.fieldName}</td><td colspan="1" class="fieldMiddle">:</td><td colspan="5" class="fieldValue">${item.fieldValue}</td></tr></#list></#if>
<#--        content2--><#if content2??><tr><!-- 水平实线 --><td colspan="10"><hr/></td></tr><#list content2 as item><tr><td colspan="4" class="fieldLabel">${item.fieldName}</td><td colspan="1" class="fieldMiddle">:</td><td colspan="5" class="fieldValue">${item.fieldValue}</td></tr></#list></#if></table>
</div>
</body>
</html>

ftl相关类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class ReceiptFieldDto {private String fieldName;private String fieldValue;
}

函数

/*** 获取ftl模板转为html*/
public static String ftlToString(Map<String, Object> map, String templateName) throws IOException,TemplateException {String value = "";Configuration configuration = new Configuration(Configuration.VERSION_2_3_32);// 模板路径String ftlPath = System.getProperty("user.dir") + File.separator + "templates";String encoding = "UTF-8";configuration.setDefaultEncoding(encoding);StringWriter out = new StringWriter();configuration.setDirectoryForTemplateLoading(new File(ftlPath));Template template = configuration.getTemplate(templateName, Locale.US, encoding);template.process(map, out);out.flush();out.close();value = out.getBuffer().toString();return value;
}/**
* html: html内容
* inputFileName: 输入文件名绝对路径
* outputFileName: 输出文件名绝对路径
* widthImage:图片宽
* heightImage:图片高
*/
public static String turnImage(String html, String inputFileName, String outputFileName, int widthImage, int heightImage) throws IOException {File inputFile = new File(inputFileName);File inputDir = inputFile.getParentFile();if (!inputFile.exists()) {if (inputDir != null) {inputDir.mkdirs();}inputFile.createNewFile();}try (BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(inputFile), StandardCharsets.UTF_8))) {String specialHtml = html.replace("&", "&amp;");bufferedWriter.write(specialHtml);bufferedWriter.newLine();}File outputFile = new File(outputFileName);File outputDir = outputFile.getParentFile();if (!outputFile.exists()) {if (outputDir != null) {outputDir.mkdirs();}outputFile.createNewFile();}Java2DRenderer renderer = new Java2DRenderer(inputFile, widthImage, heightImage);BufferedImage image = renderer.getImage();FSImageWriter imageWriter = new FSImageWriter();imageWriter.setWriteCompressionQuality(0.9f);try (FileOutputStream fout = new FileOutputStream(outputFile)) {imageWriter.write(image, fout);}return outputFileName;
}public static void deleteTempFolder(String folderPath) {FileUtils.deleteQuietly(new File(folderPath));
}private void initMap(Map<String, Object> map) {ArrayList<ReceiptFieldDto> content1List = new ArrayList<>();content1List.add(new ReceiptFieldDto("第一行标题", "第一行内容"));map.put("content1", content1List);ArrayList<ReceiptFieldDto> content2List = new ArrayList<>();content2List.add(new ReceiptFieldDto("第二行标题", "第二行内容"));map.put("content2", content2List);
}

测试

public String generateImage(){UUID uuid = UUID.randomUUID();String tempFolder = System.getProperty("user.dir") + File.separator + "tmp" + File.separator + uuid.toString().replace("-", "");try {Map<String, Object> map = new HashMap<>();initMap(map);String html = ftlToString(map, "demo.ftl");String htmlPath = tempFolder + File.separator + "demo.html";String imagePath = tempFolder + File.separator + "demo.jpg";int imageWidth = 400;int imageHeight = 600;try {turnImage(html, htmlPath, imagePath, imageWidth, imageHeight);} catch (IOException e) {e.printStackTrace();}return imagePath;} catch (Exception e) {e.printStackTrace();}finally {deleteTempFolder(tempFolder);}
}

相关文章:

springboot之HTML与图片生成

背景 后台需要根据字段动态生成HTML&#xff0c;并生成图片&#xff0c;发送邮件到给定邮箱 依赖 <!-- freemarker模板引擎--> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-freemarker</artifa…...

455. 分发饼干(LeetCode)

题目来源&#xff1a; 455. 分发饼干 - 力扣&#xff08;LeetCode&#xff09; 题目内容&#xff1a; 假设你是一位很棒的家长&#xff0c;想要给你的孩子们一些小饼干。但是&#xff0c;每个孩子最多只能给一块饼干。 对每个孩子 i&#xff0c;都有一个胃口值 g[i]&#xf…...

go设计模式

刘&#xff1a;https://www.bilibili.com/video/BV1kG411g7h4 https://www.bilibili.com/video/BV1jyreYKE8z 1. 单例模式 2. 简单工厂模式 代码逻辑&#xff1a; 原始&#xff1a;业务逻辑层 —> 基础类模块工厂&#xff1a;业务逻辑层 —> 工厂模块 —> 基础类模块…...

基于STM32的智能家居能源管理系统

1. 引言 传统家庭能源管理存在能耗监控粗放、设备联动不足等问题&#xff0c;难以适应绿色低碳发展需求。本文设计了一款基于STM32的智能家居能源管理系统&#xff0c;通过多源能耗监测、负荷预测与优化调度技术&#xff0c;实现家庭能源的精细化管理与智能优化&#xff0c;提…...

win11编译pytorchaudio cuda128版本流程

1. 前置条件 本篇续接自 win11编译pytorch cuda128版本流程&#xff0c;阅读前请先参考上一篇配置环境。 访问https://kkgithub.com/pytorch/audio/archive/refs/tags/v2.6.0.tar.gz下载源码&#xff0c;下载后解压&#xff1b; 2. 编译 在visual studio 2022安装目录下查找…...

Rust学习总结之-match

Rust 有一个叫做 match 的极为强大的控制流运算符&#xff0c;它允许我们将一个值与一系列的模式相比较&#xff0c;并根据相匹配的模式执行相应代码。模式可由字面量、变量、通配符和许多其他内容构成。 一&#xff1a;match定义 可以把 match 表达式想象成某种硬币分类器&a…...

基于Three.js的3D赛车游戏开发实战详解

目录 一、项目效果预览二、核心技术架构2.1 三维场景构建2.2 赛道与车辆模型2.3 光照系统三、核心运动系统3.1 车辆运动控制3.2 物理模拟公式3.3 边界限制四、摄像机控制系统4.1 第三人称视角数学原理4.2 鼠标交互实现五、星空背景特效5.1 点云生成算法5.2 动态闪烁效果六、性能…...

51单片机中reg52.h与regx52.h在进行位操作时的不同

reg52.h中不能使用例如 P2_0;这样的定义 而只能使用 P2^0;这样的定义 但是都不可以对位进行直接赋值操作&#xff1b; 而 regx52.h中可以使用 P2_0和P2^0&#xff1b;但是只有使用下划线的才可以对位进行赋值操作 例如P2_0 1; 但不可以是P2^0 1; 在 C 语言中&#xff0c;…...

Git GitHub基础

git是什么&#xff1f; Git是一个分布式版本控制系统&#xff0c;用于管理源代码的变更。它允许多个开发者在同一个项目上协作&#xff0c;同时跟踪每个修改的历史记录。 关键词&#xff1a; 分布式版本控制软件 软件 安装到我们电脑上的一个工具 版本控制 例如论文&…...

【Excel】 Power Query抓取多页数据导入到Excel

抓取多页数据想必大多数人都会&#xff0c;只要会点编程技项的人都不会是难事儿。那么&#xff0c;如果只是单纯的利用Excel软件&#xff0c;我还真的没弄过。昨天&#xff0c;我就因为这个在网上找了好久发好久。 1、在数据-》新建查询-》从其他源-》自网站 &#xff0c;如图 …...

视频批量分段工具

参考原文&#xff1a;视频批量分段工具 选择视频文件 当您启动这款视频批量分段工具程序后&#xff0c;有两种便捷的方式来选择要处理的视频文件。其一&#xff0c;您可以点击程序界面中的 “文件” 菜单&#xff0c;在下拉选项里找到 “选择视频文件” 按钮并点击&#xff1b…...

Redis 源码分析-内部数据结构 robj

Redis 源码分析-内部数据结构 robj Redis 中&#xff0c;一个 database 内的这个映射关系是用一个 dict 来维护的&#xff08;ht[0]&#xff09;。dict 的 key 固定用一种数据结构来表达就够了&#xff0c;即动态字符串 sds。而 value 则比较复杂&#xff0c;为了在同一个 dic…...

多通道数据采集和信号生成的模块化仪器如何重构飞机电子可靠性测试体系?

飞机的核心电子系统包括发电与配电系统&#xff0c;飞机内部所有设备和系统之间的内部数据通信系统&#xff0c;以及用于外部通信的射频设备。其他所有航空电子元件都依赖这些关键总线进行电力传输或数据通信。在本文中&#xff0c;我们将了解模块化仪器&#xff08;无论是PCIe…...

面试(进阶) —虚拟列表在什么场景使用,如何实现?

面试(进阶) —虚拟列表在什么场景使用&#xff0c;如何实现&#xff1f; 在前端开发中&#xff0c;当需要渲染大量数据时&#xff0c;传统的渲染方式往往会遇到性能瓶颈。一次性将大量数据渲染到DOM中&#xff0c;不仅会导致页面加载缓慢&#xff0c;还可能占用大量内存&#x…...

Python—Excel全字段转json文件(极速版+GUI界面打包)

目录 专栏导读1、背景介绍2、库的安装3、核心代码4、完整代码(简易版)5、进阶版(GUI)总结专栏导读 🌸 欢迎来到Python办公自动化专栏—Python处理办公问题,解放您的双手 🏳️‍🌈 博客主页:请点击——> 一晌小贪欢的博客主页求关注 👍 该系列文章专栏:请点击——…...

【Linux第一弹】Linux基础指令(上)

目录 1.ls指令 1.1 ls使用实例 2.pwd指令 3.cd指令 3.1 cd使用实例 4.touch指令 4.1touch使用实例 5.mkdir指令 5.1mkdir使用实例 6.rmdir指令和rm指令 6.1 rmdir指令使用实例->: 6.2 rm指令使用实例 7.man指令 8.cp指令 8.1 cp 使用实例 9.mv指令 9.1mv使用…...

DeepSeek教unity------UI元素长按响应

主要功能说明&#xff1a; ​长按检测&#xff1a;通过记录指针按下的时间&#xff0c;判断是否达到 longClickTime&#xff0c;从而触发长按事件。​状态管理&#xff1a;使用 StateEnum 枚举管理点击项的当前状态&#xff08;未按下、按下等待长按、长按已触发&#xff09;。…...

Netty为什么性能很高?

大家好&#xff0c;我是锋哥。今天分享关于【Netty为什么性能很高?】面试题。希望对大家有帮助&#xff1b; Netty为什么性能很高? 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 Netty是一款高性能的网络通信框架&#xff0c;主要用于构建高性能的网络应用程序。…...

[深度学习] 大模型学习2-提示词工程指北

在文章大语言模型基础知识里&#xff0c;提示词工程&#xff08;Prompt Engineering&#xff09;作为大语言模型&#xff08;Large Language Model&#xff0c;LLM&#xff09;应用构建的一种方式被简要提及&#xff0c;本文将着重对该技术进行介绍。 提示词工程就是在和LLM聊…...

MySQL 和 PostgreSQL 的详细对比

以下是 MySQL 和 PostgreSQL 的详细对比&#xff0c;帮助您根据需求选择合适的数据库&#xff1a; 1. 核心特性对比 特性MySQLPostgreSQL数据库类型关系型数据库 (RDBMS)关系型数据库 (RDBMS)&#xff0c;支持部分 NoSQL 特性SQL 标准兼容性部分兼容&#xff08;简化语法&…...

基于POI的Excel下拉框自动搜索,包括数据验证的单列删除

目录 目标 例子 1.搜索下拉框页 2.数据源页 3.效果 代码以及注意事项 1.代码 2.注意事项 1.基于Excel的话&#xff0c;相当于加入了一个【数据验证】 2.代码中的一些方法说明 目标 期望在Excel利用代码创建具备自动搜索功能的下拉框 例子 1.搜索下拉框页 2.数据源…...

Android中使用Robolectric测试点击事件(不需要手机)

文章目录 一、前言二、简单示例三、参考文档 一、前言 Robolectric 是一个由 Google 维护的开源 Android 测试框架&#xff0c;它允许你以 Android 运行时环境运行单元测试。 Robolectric 提供了一个模拟 Android 运行时环境&#xff0c;允许你测试你的代码是否正确地使用 And…...

C++Primer学习(4.7 条件运算符)

4.7 条件运算符 条件运算符( ? :)允许我们把简单的if-else逻辑嵌入到单个表达式当中&#xff0c;条件运算符按照如下形式使用: cond ? exprl : expr2; 其中cond是判断条件的表达式&#xff0c;而expr1和expr2是两个类型相同或可能转换为某个公共类型的表达式。条件运算符的执…...

Python 数据可视化(一)熟悉Matplotlib

目录 一、安装包 二、先画个折线图 1、修改标签文字和线条粗细 2、内置样式 3、scatter() 绘制散点图 4、scatter() 绘制多个点 5、设置样式 6、保存绘图 数据可视化指的是通过可视化表示来探索和呈现数据集内的规律。 一、安装包 win R 打开终端 安装 Matplotlib&…...

c#实现485协议

在C#中实现RS-485协议通信,需要结合串口(SerialPort)操作和硬件收发控制(如RTS信号切换)。以下是详细的步骤和示例代码: 1. RS-485通信原理 物理层:RS-485是差分信号标准,支持多点通信(半双工)。 收发控制:通过控制RTS(Request to Send)或DTR引脚切换发送/接收模式…...

考研出分24小时,人类精神状态图鉴

2月24日&#xff0c;上午10点起&#xff0c;各省考研初试成绩陆续公布&#xff0c;考生们或紧张的输入准考证号&#xff0c;或抱团等待“审判”。然而更魔幻的还在后头——下午4点&#xff0c;教育部竟在同一天直接发布了《2025年研考国家分数线》。 不少网友表示&#xff1a;…...

神经网络AI原理回顾

长期记忆存储在大模型的参数权重中&#xff0c;不经过推理和编码无法读取&#xff0c;且必须依赖输入的提示&#xff0c;因为大模型不会无缘无故的自言自语&#xff0c;毕竟输入层是它唯一 与外界交互的窗口。 目前个性化大模型的局限就是训练成本过高&#xff0c;除非使用RAG&…...

不同数据类型在数据库和编程语言之间的对应关系表

不同数据类型在数据库和编程语言之间的对应关系表 MySql 与 C# MySqlC#varcharstringbigintlongbigint unsignedulongintintint unsigneduintsmallintshortsmallint unsignedushortVARCHAR(36)GuidsmalldatetimeDateTimedateDateTimedatetimeDateTimetimestampDateTimefloatf…...

数据库导出

MySQL数据库 使用命令行导出 导出整个数据库&#xff1a;在命令行中输入mysqldump -u用户名 -p密码 数据库名 > 导出文件路径/文件名.sql。例如mysqldump -uroot -p123456 mydb > /home/user/mydb_backup.sql&#xff0c;回车后输入密码即可将名为mydb的数据库导出为SQL…...

进程间通信 —— 共享内存

目录 1.共享内存实现通信的原理 2.如何使用共享内存实现通信 共享内存通信接口介绍 shmget shmat shmdt shmctl 使用示例 key和shmid 3.共享内存通信的优缺点 缺点&#xff1a;不提供任何同步机制&#xff0c;可能会造成数据混乱。 优点&#xff1a;共享内存是进程…...