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);
	}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/980207.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于YOLO11深度学习的遥感视角农田检测与分割系统【python源码+Pyqt5界面+数据集+训练代码】深度学习实战、目标分割、人工智能

《------往期经典推荐------》 一、AI应用软件开发实战专栏【链接】 项目名称项目名称1.【人脸识别与管理系统开发】2.【车牌识别与自动收费管理系统开发】3.【手势识别系统开发】4.【人脸面部活体检测系统开发】5.【图片风格快速迁移软件开发】6.【人脸表表情识别系统】7.【…

RabbitMQ面试题及原理

RabbitMQ使用场景&#xff1a; 异步发送&#xff08;验证码、短信、邮件…&#xff09;MYSQL和Redis, ES之间的数据同步分布式事务削峰填谷 1. 消息可靠性&#xff08;不丢失&#xff09; 消息丢失场景&#xff1a; RabbitMQ-如何保证消息不丟失&#xff1f; 开启生产者确…

Python每日一练:学习指南进行汇总

Python&#xff0c;一种“优雅”、“明确”、“简单”的编程语言&#xff0c;凭借其低学习曲线、强大的开源生态系统、卓越的平台可移植性以及面向对象和函数式编程的支持&#xff0c;成为了众多开发者首选。 01 Python 应用领域和就业形势分析 Python&#xff0c;一种“优雅…

商米科技前端工程师(base上海)内推

1.根据原型或高保真设计&#xff0c;开发web、H5、小程序等类型的前端应用&#xff1b; 2.在指导下&#xff0c;高质量完成功能模块的开发&#xff0c;并负责各功能模块接口设计工作&#xff1b; 3.负责产品及相关支撑系统的开发及维护工作&#xff0c;不断的优化升级&#x…

八. Spring Boot2 整合连接 Redis(超详细剖析)

八. Spring Boot2 整合连接 Redis(超详细剖析) 文章目录 八. Spring Boot2 整合连接 Redis(超详细剖析)2. 注意事项和细节3. 最后&#xff1a; 在 springboot 中 , 整合 redis 可以通过 RedisTemplate 完成对 redis 的操作, 包括设置数据/获取数据 比如添加和读取数据 具体…

【漫话机器学习系列】113.逻辑回归(Logistic Regression) VS 线性回归(Linear Regression)

逻辑回归 vs 线性回归&#xff1a;详解对比 在机器学习和统计学中&#xff0c;逻辑回归&#xff08;Logistic Regression&#xff09; 和 线性回归&#xff08;Linear Regression&#xff09; 都是非常常见的模型。尽管它们的数学表达式有一定的相似性&#xff0c;但它们的应用…

构建智能 SQL 查询代理agent,把整个查询过程模块化,既能自动判断使用哪些表,又能自动生成 SQL 语句,最终返回查询结果

示例代码&#xff1a; import os import getpass from dotenv import load_dotenv from pyprojroot import here from typing import List from pprint import pprint from pydantic import BaseModel from langchain_core.tools import tool from langchain_core.runnables i…

fastapi中的patch请求

目录 示例测试使用 curl 访问&#xff1a;使用 requests 访问&#xff1a;预期返回&#xff1a; 浏览器访问 示例 下面是一个使用 app.patch("") 的 FastAPI 示例&#xff0c;该示例实现了一个简单的用户信息更新 API。我们使用 pydantic 定义数据模型&#xff0c;并…

【文献阅读】Collective Decision for Open Set Recognition

基本信息 文献名称&#xff1a;Collective Decision for Open Set Recognition 出版期刊&#xff1a;IEEE TRANSACTIONS ON KNOWLEDGE AND DATA ENGINEERING 发表日期&#xff1a;04 March 2020 作者&#xff1a;Chuanxing Geng and Songcan Chen 摘要 在开集识别&#xff0…

Hadoop之02:MR-图解

1、不是所有的MR都适合combine 1.1、map端统计出了不同班级的每个学生的年龄 如&#xff1a;(class1, 14)表示class1班的一个学生的年龄是14岁。 第一个map任务&#xff1a; class1 14 class1 15 class1 16 class2 10第二个map任务&#xff1a; class1 16 class2 10 class…

代码随想录Day23 | 39.组合总和、40.组合总和II、131.分割回文串

39.组合总和 自己写的代码&#xff1a; class Solution { public:vector<int> path;vector<vector<int>> res;int sum0;void backtracking(vector<int>& candidates,int target,int startIndex){if(sum>target) return;if(sumtarget){res.pus…

【MySQL】索引(页目录、B+树)

文章目录 1. 引入索引2. MySQL与磁盘交互的基本单位3. 索引的理解3.1 页目录3.2 B树 4. 聚簇索引、非聚簇索引5. 索引的操作5.1 索引的创建5.1.1 创建主键索引5.1.2 创建唯一索引5.1.3 普通索引的创建5.1.4 全文索引的创建 5.2 索引的查询5.3 删除索引 1. 引入索引 索引&#…

132. 分割回文串 II

简单分析 输入的参数是字符串s&#xff0c;返回值是最小的切割次数。那这个问题的典型解法应该是动态规划&#xff0c;因为我们需要找最优解&#xff0c;而每一步的选择可能会影响后面的结果&#xff0c;但可以通过子问题的最优解来构建整体最优解。 那么动态规划的状态如何定…

CSS定位详解

1. 相对定位 1.1 如何设置相对定位&#xff1f; 给元素设置 position:relative 即可实现相对定位。 可以使用 left 、 right 、 top 、 bottom 四个属性调整位置。 1.2 相对定位的参考点在哪里&#xff1f; 相对自己原来的位置 1.3 相对定位的特点&#xff1…

NLP11-命名实体识别(NER)概述

目录 一、序列标注任务 常见子任务 二、 命名实体识别&#xff08;NER&#xff09; &#xff08;一&#xff09;简介 &#xff08;二&#xff09;目标 &#xff08;三&#xff09;应用场景 &#xff08;四&#xff09;基本方法 &#xff08;五&#xff09;工具与资源 一…

基于SQL数据库的酒店管理系统

一、数据库设计 1&#xff0e;需求分析 客房的预定&#xff1a;可以通过网络进行预定&#xff0c;预定修改&#xff0c;取消预订。 客房管理&#xff1a;预定管理、客房查询、设置房态、开房、换房、续住、退房等管理。 员工管理: 员工修改信息、人员调配。 账务管理&…

2024年中国城市统计年鉴(PDF+excel)

2024年中国城市统计年鉴&#xff08;PDFexcel&#xff09; 说明&#xff1a;包括地级县级市 格式&#xff1a;PDFEXCEL 《中国城市统计年鉴》是一部全面反映中国城市发展状况的官方统计出版物&#xff0c;包括各级城市的详细统计数据。这部年鉴自1985年开始出版&#xff0c;…

1.C语言初识

C语言初识 C语言初识基础知识hello world数据类型变量、常量变量命名变量分类变量的使用变量的作用域 常量字符字符串转义字符 选择语句循环语句 函数&#xff1b;数组函数数组数组下标 操作符操作符算术操作符移位操作符、位操作符赋值操作符单目操作符关系操作符逻辑操作符条…

LINUX基础 - 网络基础 [一]

前言 在当今的数字化世界中&#xff0c;网络已成为计算机系统和应用的核心组成部分。Linux&#xff0c;作为一个开放源代码的操作系统&#xff0c;在服务器、嵌入式设备、以及开发环境中被广泛使用&#xff0c;而其强大的网络能力使其在网络管理和网络编程领域占据了重要地位。…

苹果廉价机型 iPhone 16e 影像系统深度解析

【人像拍摄差异】 尽管iPhone 16e支持后期焦点调整功能&#xff0c;但用户无法像iPhone 16系列那样通过点击屏幕实时切换拍摄主体。前置摄像头同样缺失人像深度控制功能&#xff0c;不过TrueTone原彩闪光灯系统在前后摄均有保留。 很多人都高估了 iPhone 的安全性&#xff0c;查…