《SpringBoot2.0 实战》系列-整合thymeleaf 实现模板文件转图片_spring-boot 怎么将 thymeleaf转图片-程序员宅基地

技术标签: 动态模板转图片  springboot2.0实战  

前言

之前写了两篇关于动态模板转pdf和word的文章:《模板文件转pdf打印》《模板文件转word打印》,最近又接到一个需求需要转图片,所以本文做下记录。

如何开始

thymeleaf 依赖包

<!-- thymeleaf -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!--html2image-->
<dependency>
    <groupId>gui.ava</groupId>
    <artifactId>html2image</artifactId>
    <version>2.0.1</version>
</dependency>

thymeleaf配置

spring:
  # thymeleaf
  thymeleaf:
    prefix: classpath:/templates/
    check-template-location: true
    suffix: .html
    encoding: UTF-8
    mode: HTML
    cache: false
    servlet:
      content-type: text/html

模板准备,此处模板和导出pdf、word的模板一样。

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.w3.org/1999/xhtml" layout:decorator="layout">
<head lang="en">
    <title>Spring Boot Demo - PDF</title>
    <style>
        @page {
    
            size: 210mm 297mm; /*设置纸张大小:A4(210mm 297mm)、A3(297mm 420mm) 横向则反过来*/
            margin: 0.25in;
            padding: 1em;
            @bottom-center{
    
                content:"葫芦科技  版权所有";
                font-family: SimSun;
                font-size: 12px;
                color:red;
            };
            @top-center {
     content: element(header) };
            @bottom-right{
    
                content:"第" counter(page) "页  共 " counter(pages) "页";
                font-family: SimSun;
                font-size: 12px;
                color:#000;
            };
        }
        body{
    font-family: 'SimSun'}
        h2{
    color: crimson}
        #myheader{
    
            width: 500px;
            height: 22px;
            border: 1px solid #000000;
        }
        table, th , td  {
    
            border: 1px solid grey;
            border-collapse: collapse;
            padding: 5px;
        }
        table tr:nth-child(odd) {
    
            background-color: #f1f1f1;
        }
        table tr:nth-child(even) {
    
            background-color: #ffffff;
        }
        #input1{
    
            border-bottom: 1px solid #000000;
        }
    </style>
</head>
<!--这样配置不中文不会显示-->
<!--<body style="font-family: 宋体">-->
<body style="font-family: 'SimSun'">
<div>1.标题-中文</div>
<h2 th:text="${title}"></h2>

<div>2.按钮:按钮的边框需要写css渲染</div>
<button class="a" style="border: 1px solid #000000"> click me t-p</button>
<div id="divsub"></div>

<div>3.普通div</div>
<div id="myheader">Alice's Adventures in Wonderland</div>

<div>4.图片 绝对定位到左上角(注意:图片必须用全路径或者http://开头的路径,否则无法显示)</div>
<img th:src="${imageUrl}"/>

<div>5.普通table表格</div>
<div>
    <table style="width: 700px">
        <tr>
            <th>姓名</th>
            <th>昵称</th>
            <th>年龄</th>
        </tr>
        <tr th:each="info : ${demoList}">
            <td th:text="${info.name}"></td>
            <td th:text="${info.nick}"></td>
            <td th:text="${info.age}"></td>
        </tr>
    </table>

</div>

<div>6.input控件,边框需要写css渲染 (在模板中一般不用input,因为不存在输入操作)</div>
<div>
    <label>姓名:</label>
    <input id="input1" aria-label="葫芦胡" type="text" value="葫芦胡"/>
</div>
</body>
</html>

文件位置

模板位置放在templates目录下,宋体包放在static目录下,如下:
在这里插入图片描述

核心处理类

工具类

/**
 * @Description html模板转jpg
 * @Author gourd.hu
 * @Date 2020/6/28 10:43
 * @Version 1.0
 */
@Slf4j
public class ImageUtil {
    
    public static void saveAsImage(TemplateEngine templateEngine, String templateName, Map<String,Object> variables, String filePath){
    
        // 声明一个上下文对象,里面放入要存到模板里面的数据
        final Context context = new Context();
        context.setVariables(variables);
        //imageHtml为获取的html源码字符串
        String imageHtml = templateEngine.process(templateName, context);
        Html2Image html2Image = Html2Image.fromHtml(imageHtml);
        ImageRenderer imageRenderer = html2Image.getImageRenderer();
        imageRenderer.saveImage(filePath);
    }

    public static void download(TemplateEngine templateEngine, String templateName, Map<String,Object> variables, HttpServletResponse response, String fileName ){
    
        // 断言参数不为空
        ResponseEnum.TEMPLATE_DATA_NULL.assertNotEmpty(variables);
        // 声明一个上下文对象,里面放入要存到模板里面的数据
        final Context context = new Context();
        context.setVariables(variables);
        // 设置编码、文件ContentType类型、文件头、下载文件名
        response.setCharacterEncoding("utf-8");
        response.setContentType("image/jpeg");
        ServletOutputStream outputStream = null;
        try {
    
            response.setHeader("Content-Disposition", "attachment;fileName=" +
                    new String(fileName.getBytes("gb2312"), "ISO8859-1"));
            outputStream = response.getOutputStream();
        } catch (UnsupportedEncodingException e) {
    
            log.error(e.getMessage(), e);
        } catch (IOException e) {
    
            log.error(e.getMessage(), e);
        }
        //imageHtml为获取的html源码字符串
        String imageHtml = templateEngine.process(templateName, context);
        Html2Image html2Image = Html2Image.fromHtml(imageHtml);
        ImageRenderer imageRenderer = html2Image.getImageRenderer();
        imageRenderer.saveImage(outputStream,Boolean.TRUE);
    }
}

controller测试入口

 /**
     * image下载到特定位置
     *
     */
    @GetMapping(value = "/image/save")
    @ApiOperation(value="image下载到特定位置")
    public BaseResponse<String> imageSave() {
    
        Map<String,Object> variables = new HashMap<>(4);
        variables.put("title","image下载到特定位置!");
        variables.put("imageUrl",sslEnabled?"https://localhost:10001/imgs/sg.jpg":"http://localhost:10001/imgs/sg.jpg");
        List<Map<String,String>> demoList = new ArrayList<>();
        Map<String,String> demoMap = new HashMap<>(8);
        demoMap.put("name","哈哈");
        demoMap.put("nick","娃娃");
        demoMap.put("age","19");
        Map<String,String> demoMap2 = new HashMap<>(8);
        demoMap2.put("name","天天");
        demoMap2.put("nick","饭饭");
        demoMap2.put("age","14");
        demoList.add(demoMap);
        demoList.add(demoMap2);
        variables.put("demoList",demoList);
        // pdf文件下载位置
        String pdfPath = CommonUtil.isLinux() ? pdfLinuxPath : pdfWindowsPath +  "test0.png";
        ImageUtil.saveAsImage(templateEngine,"pdfPage",variables,pdfPath);
        return BaseResponse.ok("image保存成功");
    }

    /**
     * image浏览器下载
     *
     */
    @GetMapping(value = "/image/download")
    @ApiOperation(value="image浏览器下载")
    public BaseResponse<String> imageDownload(HttpServletResponse response) {
    
        Map<String,Object> variables = new HashMap<>(4);
        variables.put("title","image浏览器下载!");
        variables.put("imageUrl",sslEnabled?"https://localhost:10001/imgs/sg.jpg":"http://localhost:10001/imgs/sg.jpg");
        List<Map<String,String>> demoList = new ArrayList<>();
        Map<String,String> demoMap = new HashMap<>(8);
        demoMap.put("name","哈哈");
        demoMap.put("nick","娃娃");
        demoMap.put("age","19");
        Map<String,String> demoMap2 = new HashMap<>(8);
        demoMap2.put("name","天天");
        demoMap2.put("nick","饭饭");
        demoMap2.put("age","14");
        demoList.add(demoMap);
        demoList.add(demoMap2);
        variables.put("demoList",demoList);
        ImageUtil.download(templateEngine,"pdfPage",variables,response,"test.jpg");
        return BaseResponse.ok("image保存成功");
    }

避坑

在Linux服务器上,图片会出现乱码情况,原因是lunix上没有宋体的字体包,我们需要将宋体包simsun.ttf 上传到Linux服务器的 /usr/share/fonts 目录下,如图:
在这里插入图片描述

测试效果

在这里插入图片描述

结语

至此,导出图片功能就完成了,如果本文有错误的地方,欢迎评论指正。

===============================================

代码均已上传至本人的开源项目
cloud-plus:https://blog.csdn.net/HXNLYW/article/details/104635673

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/HXNLYW/article/details/107014091

智能推荐

「Head First」编程系列丛书_head first 系列-程序员宅基地

文章浏览阅读9.4k次,点赞24次,收藏20次。深入浅出系列书籍是由O’Reilly出版社发行的一系列教育书籍,它强调以特殊的方式排版,由大量的图片和有趣的内容组合构成,而达到非疲劳的沉浸式学习效果。整理了一部分Head First书籍,有需要的朋友可以自行下载1、O’Reilly:Head First设计模式《O’Reilly:Head First设计模式》趋近完美,因为它在提供专业知识的同时,仍然具有相当高的可读性。本书共有14章,每..._head first 系列

Please check CUDA_ARCH_PTX or CUDA_ARCH_BIN in your build configuration错误解决-程序员宅基地

文章浏览阅读4.1k次。在RTX2060显卡的机器上,编译了opencv4.4,报错如下:classes, confidences, boxes = net.detect(image, confThreshold, nmsThreshold)cv2.error: OpenCV(4.4.0) /home/ycc/opencv-4.4.0/modules/dnn/src/dnn.cpp:2353: error: (-216:No CUDA support) OpenCV was not built to work with th_cuda_arch_ptx

Spring 命名bean的错误_关于bean的命名规则下面说法错误的是 bean name 可以通过-程序员宅基地

文章浏览阅读460次。错误提示:没有定义这个personService bean 可能是这个bean打错了org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'personService' is defined_关于bean的命名规则下面说法错误的是 bean name 可以通过

UART简介-程序员宅基地

文章浏览阅读5.8k次,点赞2次,收藏17次。UART简介1) UART简介UART(Universal Asynchronous Receiver Transmitter),即通用异步(收发双发时钟不同步)收发器,是一种通用的串行、异步通信总线,该总线有两条数据线,可以实现全双工的发送和接收,在嵌入式系统中常用于主机与辅助设备之间的通信。2) 并行与串行通信总线的本质就是导线,总线的作用是在计算机不同部件间传递一些信息,总线传递的本质就是一些高低电平的信号,比如用高电平信号代表1,低电平信号代表0。所谓并行通信指总线在传递信号的时候把数据_uart

python scapy修改替换Pcap包负载_scapy 恶意流量转化为图像-程序员宅基地

文章浏览阅读657次。在恶意流量种类的选择方面,经过特征选择,最终得到某种恶意流量的特征存在于负载当中,其中包含一些恶意的网站等,接收到这种恶意流量后会被感染,并变成恶意流量发送端。基于此,采用更换负载内容以达到增加恶意流量数据的目的。使用scapy库进行对数据包的解析,以及希望通过更改负载内容从而生成新的恶意流量数据。_scapy 恶意流量转化为图像

SAP 公司间采购之免费订单和退货订单_sap 公司间转储 免费-程序员宅基地

文章浏览阅读4.2k次,点赞2次,收藏25次。公司间采购订单的退货以及免费项目两种特殊的公司间采购订单业务:公司间采购订单退货:有正常的业务就会有退货业务; 公司间免费的采购订单:譬如A公司的废料可能无偿调拨给集团下另外一家公司。1.公司间采购订单退货在正常的公司间采购订单的配置基础上,只需要增加一个配置:分配退货的发货单类型NCR给相应的采购订单类型和交货工厂(供应商对应的工厂)。配置路径:OLME>Purchase order>Return Order>Store Return/Return Plant to _sap 公司间转储 免费

随便推点

GIS底层开发、GIS前端开发和GIS后端开发有什么区别?_qianduan gis技术-程序员宅基地

文章浏览阅读1.6k次。说白了就是做软件,国内外有各种GIS软件公司,例如美国的ESRI公司的arcgis,国产的超图的supermap和中地数码的mapgis等,都属于gis软件。软件开发又分为应用层和底层,通常会用到c语言或c++,企业会招聘C++工程师来从事这部分工作。需要这类开发人员掌握基本的数据结构和算法以及相关开发经验。_qianduan gis技术

使用git commit时‘“node“‘ 不是内部或外部命令,也不是可运行的程序_git提交报错:'"node"' 不是内部或外部命令,也不是可运行的程序 或批处理文件。 hu-程序员宅基地

文章浏览阅读2.5k次。项目是使用了husky,在使用git comit的时候,会调用pre-commit的node代码,这里找不到对应的node环境变量,所以报错了。,换一个有配置husky的项目,执行git commit 发现一样的结果,这又排除了是项目的配置被人修改导致的问题。,使用git BASH命令行工具来执行git commit,结果还是一样,这说明不是vscode的问题。可以看到版本号,这就说明,node的环境变量配置并没有问题。这比肯德基疯狂星期四还离谱好不!把那个分号去掉,试了一下,就可以了!..._git提交报错:'"node"' 不是内部或外部命令,也不是可运行的程序 或批处理文件。 hu

高性能 TCP/UDP 通信框架 HP-Socket_hpsocket udpcast udpnode-程序员宅基地

文章浏览阅读1.9k次。HP-Socket 是一套通用的高性能 TCP/UDP 通信框架,包含服务端组件、客户端组件和Agent组件,广泛适用于各种不同应用场景的 TCP/UDP 通信系统,提供 C/C++、C#、Delphi、E(易语言)、Java、Python 等编程语言接口。HP-Socket 对通信层实现完全封装,应用程序不必关注通信层的任何细节;HP-Socket 提供基于事件通知模型的 API 接口,能非_hpsocket udpcast udpnode

详解ZStack Cloud v4.0:前端架构探秘之低代码开发-程序员宅基地

文章浏览阅读650次。日前,ZStack Cloud v4.0发布,相信大家看了都有一种焕然一新的感觉,本轮发布最大的变化是 UI 端进行了完全的重写,技术栈由原来的 Vue 体系升级到 React,其中采用了一些全新的技术,如 UMI、GraphQL、微前端、低代码开发等,接下来,我们一起来探秘ZStack Cloud v4.0 漂亮 UI 的技术原理,本文将为您解读:低代码开发。一、为何要引入低代码开发?在新版 UI 中,所有列表页、权限控制、主题外观、界面文字都不是由前端开发人员手动编写的,而是借助低代码开发平台根据响

ITEXT table的单元格边框设置_itext实现表格边框-程序员宅基地

文章浏览阅读1.7w次,点赞10次,收藏18次。通过PdfPCell的方法可以隐藏单元格的边框。//新建单元格对象PdfPCell cell = new PdfPCell(new Phrase("test"));//隐藏上边框// cell.disableBorderSide(1);//隐藏下边框// cell.disableBorderSide(2);//隐藏上、下边框//cell.disableBorderSide(3..._itext实现表格边框

概念数据模型,逻辑数据模型,物理数据模型-程序员宅基地

文章浏览阅读528次。在数据仓库领域有一个概念叫conceptual data model,中文一般翻译为“概念数据模型”。 概念数据模型是最终用户对数据存储的看法,反映了最终用户综合性的信息需求,它以数据类的方式描述企业级的数据需求,数据类代表了在业务环境中自然聚集成的几个主要类别数据。 概念数据模型的内容包括重要的实体及实体之间的关系。在概念数据模型中不包括实体的属性,也不用定义实体的主键。这是概..._逻辑数据模型转为物理数据模型时没有dbm

推荐文章

热门文章

相关标签