Spring Boot 中使用 Poi-tl 渲染数据并生成 Word 文档

本文 Demo 已收录到 demo-for-all-in-java 项目中,欢迎大家 star 支持!后续将持续更新!

前言

产品经理急冲冲地走了过来。「现在需要将按这些数据生成一个 Word 报告文档,你来安排下」

项目中有这么一个需求,需要将用户填写的数据填充到一个 Word 文档中,而这个 Word 文档是人家给定了的。换句话说,让你按照这个文档的内容格式生成新的文档。

什么是 Poi-tl ?

官网:http://deepoove.com/poi-tl/1.9.x/

poi-tl(poi template language)是一种 Word 模板引擎,可以基于 Word 模板和数据生成新的文档,它的底层是通过 Apache POI 来实现的。

Apache POI 不仅封装了易用的文档 API (文本、图片、表格、页眉、页脚、图表等),也可以在底层直接操作文档XML结构。

poi-tl 拥有如下特性(了解瞄一眼就行):

内容描述
文本将标签渲染为文本
图片将标签渲染为图片
表格将标签渲染为表格
列表将标签渲染为列表
图表条形图(3D条形图)、柱形图(3D柱形图)、面积图(3D面积图)、折线图(3D折线图)、雷达图、饼图(3D饼图)等图表渲染
If Condition判断隐藏或者显示某些文档内容(包括文本、段落、图片、表格、列表、图表等)
Foreach Loop循环循环某些文档内容(包括文本、段落、图片、表格、列表、图表等)
Loop表格行循环渲染表格的某一行
Loop表格列循环渲染表格的某一列
Loop有序列表支持有序列表的循环,同时支持多级列表
图片替换将原有图片替换成另一张图片
书签、锚点、超链接支持设置书签,文档内锚点和超链接功能
强大的表达式完全支持SpringEL表达式,可以扩展更多的表达式:OGNL, MVEL…
标签定制支持自定义标签前后缀
文本框文本框内标签支持
样式模板即样式,同时代码也可以设置样式
模板嵌套模板包含子模板,子模板再包含子模板
合并Word合并Merge,也可以在指定位置进行合并
用户自定义函数(插件)在文档任何位置执行函数

我们就可以使用这个它来实现这个需求。

如何使用 Poi-tl ?

本篇文章将以 Spring Boot 项目作为演示,屏幕前的朋友们可以一起跟着我的步骤来,实践一番!

  1. 首先创建一个 Spring Boot 项目,版本目前我的 Demo 是 2.2.1,你可以更改你的 Spring Boot 版本,那现在我这里已经创建好了。

其中, pom.xml 只有两个依赖项,一个 web 和一个 test :

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Boot Test 依赖 -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope>
</dependency>
  1. 接着在 pom.xml 中引入 Poi-tl 的依赖项
<!-- Poi-tl Word 模板引擎-->
<dependency><groupId>com.deepoove</groupId><artifactId>poi-tl</artifactId><version>1.9.1</version>
</dependency>
  1. 准备一个 Word 模板

这一步你可以自己动手做一个 Word 模板,这里我先演示下。就先创建一个名为 Hello World.docx 的 Word 文档,模板内容如下:

找一个你喜欢的位置存放这个模板,我现在把它放到项目的 resource 目录。

{{title}} 这种由两个大括号包住的,目前可以看成占位符,这个模板中有 4 个占位符,后续的数据就渲染到这些地方上。

  1. 获取模板所在的路径,并将数据渲染到模板上

渲染只需一行代码,就是使用 XWPFTemplate 的 API 就可以了,通过 complierender 方法,就可以将数据渲染到模板中,得到渲染好的新文档。

@SpringBootTest
public class PoiTlApplicationTest {@Testpublic void test() {// 获取 Word 模板所在路径String filepath = this.getClass().getClassLoader().getResource("hello-world.docx").getPath();// 通过 XWPFTemplate 编译文件并渲染数据到模板中XWPFTemplate template = XWPFTemplate.compile(filepath).render(new HashMap<String, Object>(){{put("title", "Hello, poi-tl Word模板引擎");put("text", "Hello World");put("author", "god23bin");put("description", "这还不关注 god23bin ?再不关注我可要求你关注了!");}});try {// 将完成数据渲染的文档写出template.writeAndClose(new FileOutputStream("output.docx"));} catch (IOException e) {e.printStackTrace();}}}

执行这个单元测试,就可以看到在项目所在目录下输出了新的文档 output.docx,打开这个文档,我们就可以看到如下图所示的内容:

大功告成,这就是渲染好的新文档了,是不是很简单,一行代码就完成了根据模板进行数据的渲染!

相关概念

模板

模板是 Docx 格式的 Word 文档,我们可以使用 Microsoft office、WPS Office 等软件来制作模板。

标签

上面说到 {{title}} 这种理解成占位符,实际上,官方是称之为「标签」。

所有的标签都是以 {{ 开头,以 }} 结尾,标签可以出现在任何位置,包括页眉,页脚,表格内部,文本框等。

表格布局可以设计出很多优秀专业的文档,推荐使用表格布局。

poi-tl 模板遵循 所见即所得 的设计,模板和标签的样式会被完全保留,就如我上面演示的,一级标题和字体颜色的样式就被保留下来了。

数据模型

数据模型,也就是我们需要渲染到模板中的数据,可以是哈希表,也可以是普通的 Java 对象。

  1. 哈希表(key 名是标签名):
Map<String, Object> data = new HashMap<>();
data.put("title", "Hello, poi-tl Word模板引擎");
data.put("text", "Hello World");
data.put("author", "god23bin");
data.put("description", "这还不关注 god23bin ?再不关注我可要求你关注了!");
  1. Java 对象(属性名是标签名):
public class DataModel {private String title;private String text;private String author;private String description;// 省略 getter 和 setter
}DataModel data = new DataModel();
data.setTitle("Hello, poi-tl Word模板引擎");
data.setText("Hello World");
data.setAuthor("god23bin");
data.setDescription("这还不关注 god23bin ?再不关注我可要求你关注了!");

有了哈希表和或者 Java 对象的数据模型后,将这个数据丢给渲染的 API,就可以完成数据的渲染了。

标签的写法

poi-tl 里只有标签,那么我们需要知道标签的写法是怎样的。在 Word 文档里,可以有:文本、图片、表格、列表等元素,那么对应的,咱们的标签也有这些。

文本标签 {{var}}

简单粗暴,直接 {{标签名}} 就是文本标签了。

图片标签 {{@var}}

{{@标签名}} 就是图片标签,@ 标识了这个标签的类型是图片,其他的也是同理,不同的符号标识不同类型的标签。

表格标签 {{#var}}

使用 # 标识这是一个表格标签。

列表标签 {{*var}}

使用 * 标识这是一个列表标签。

其余的标签

剩下的标签还有很多,详细的内容你可以阅读官方文档,这里就不一一介绍了。

下面我将写下我用过的内容。

插件

插件,又称为自定义函数,它允许我们在模板标签位置处执行预先定义好的函数。由于插件机制的存在,我们几乎可以在模板的任何位置执行任意操作。

插件是 poi-tl 的核心,默认的标签和引用标签都是通过插件加载。

默认插件

poi-tl 默认提供了八个策略插件,用来处理文本、图片、列表、表格、文档嵌套、引用图片、引用多系列图表、引用单系列图表等:

  • TextRenderPolicy
  • PictureRenderPolicy
  • NumberingRenderPolicy
  • TableRenderPolicy
  • DocxRenderPolicy
  • MultiSeriesChartTemplateRenderPolicy
  • SingleSeriesChartTemplateRenderPolicy
  • DefaultPictureTemplateRenderPolicy

由于这 8 个插件是经常用到的,所以这些插件被注册为不同的标签类型,也就是我们看到过的 {{var}}、{{@var}}、{{#var}} 等不同类型的标签,从而搭建了 poi-tl 的标签体系。

除了这 8 个通用的策略插件外,还内置了一些额外用途的插件:

DynamicTableRenderPolicy动态表格插件,允许直接操作表格对象示例-动态表格
HackLoopTableRenderPolicy循环表格行,下文会详细介绍示例-表格行循环
LoopColumnTableRenderPolicy循环表格列示例-表格列循环
BookmarkRenderPolicy书签和锚点示例-Swagger文档
JSONRenderPolicy高亮显示JSON代码块示例-Swagger文档
AbstractChartTemplateRenderPolicy引用图表插件,允许直接操作图表对象
ParagraphRenderPolicy渲染一个段落,可以包含不同样式文本,图片等
DocumentRenderPolicy渲染多个段落和表格
TOCRenderPolicyBeta实验功能:目录,打开文档时需要更新域

使用插件

为了让插件在某个标签处执行,我们需要将插件与标签绑定

当我们有个模板标签为 {{description}},默认是文本标签,如果希望在这个位置做些不一样或者更复杂的事情,我们可以将插件应用到这个模板标签,比如渲染 HTML:

ConfigureBuilder builder = Configure.builder();
builder.bind("description", new HtmlRenderPolicy());

此时,{{description}} 将不再是一个文本标签,而是一个自定义的支持 HTML 渲染的标签。

当然,这里的 HTML 渲染的插件,默认是没有提供的,需要引入以下的依赖项,才能支持 HTML 的渲染。

<!-- 支持渲染 HTML 的插件 -->
<dependency><groupId>io.github.draco1023</groupId><artifactId>poi-tl-ext</artifactId><version>0.3.3</version>
</dependency>

示例:我们对 {{author}} 这个标签绑定上支持 HTML 渲染的插件,这样就能渲染 HTML 的文本了。

@SpringBootTest
public class PoiTlApplicationTest {@Testpublic void test() {// 获取 Word 模板所在路径String filepath = this.getClass().getClassLoader().getResource("hello-world.docx").getPath();// 给标签绑定插件Configure configure = Configure.builder().bind("author", new HtmlRenderPolicy()).build();// 通过 XWPFTemplate 编译文件并渲染数据到模板中XWPFTemplate template = XWPFTemplate.compile(filepath, configure).render(new HashMap<String, Object>(){{put("title", "Hello, poi-tl Word模板引擎");put("text", "Hello World");put("author", "<h2>god23bin</h2>");put("description", "这还不关注 god23bin ?再不关注我可要求你关注了!");}});try {// 将完成数据渲染的文档写出template.writeAndClose(new FileOutputStream("output.docx"));} catch (IOException e) {e.printStackTrace();}}}

生成的 Word 文档如下图所示,可以看到 {{author}} 这个标签的 HTML 文本已经渲染成功了!(蓝框框)

表格行循环

当有类似如下需求的时候,在表格里展示多行同类型的数据,那么就需要用到表格的行循环了。

这里也是涉及到插件的,具体就是使用 HackLoopTableRenderPolicy 这个插件策略,这个策略能够根据集合数据进行循环渲染,这样就渲染数据到表格的行上了,集合有多少个元素,那么就有多少行。

我们来看下这个表格行循环的模板是怎样写的,是这样的:

可以看到 {{articles}}{{columns}} 是标准的文本标签,我们这里将这两个标签置于循环行的上一行,循环行里设置要循环的标签和内容,注意这里的标签是使用 [] 的,以此来区分标准的标签语法。

同时 {{articles}}{{columns}} 标签对应的数据就是文章和专栏的集合。

我们写一个该模板的数据模型,以 Java 对象来写,同时模拟数据从数据库中读取。

数据模型:AcWordModel

public class AcWordModel {/*** 文章明细数据模型-表格行循环*/private List<Article> articles;/*** 专栏明细数据模型*/private List<SpecialColumn> columns;// 省略 getter 和 setter
}

其中的 Article 和 SpecialColumn 模型如下:

public class Article {private String title;private String tags;private Integer reading;private Integer likes;// 省略 getter 和 setter
}
public class SpecialColumn {private String name;private Integer subscription;private Integer nums;// 省略 getter 和 setter
}

进行测试,获取数据和模板,让标签和表格行循环的插件进行绑定

    @Testpublic void rowLoopTest() {// 获取数据,这里假装是从数据库中查询得到的AcWordModel data = getFromDB();// 获取 Word 模板所在路径String filepath = this.getClass().getClassLoader().getResource("table-row-loop.docx").getPath();// 给标签绑定插件,这里就绑定表格行循环的插件Configure configure = Configure.builder().bind("articles", new HackLoopTableRenderPolicy()).bind("columns", new HackLoopTableRenderPolicy()).build();// 通过 XWPFTemplate 编译文件并渲染数据到模板中XWPFTemplate template = XWPFTemplate.compile(filepath, configure).render(data);try {// 将完成数据渲染的文档写出template.writeAndClose(new FileOutputStream("ac-word.docx"));} catch (IOException e) {e.printStackTrace();}}

这样,就能实现表格的行循环了!

封装 Word 渲染生成新文档的工具

我们可以再封装下这个 API,写一个工具类,如下:

package cn.god23bin.demo.util;import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Map;public class WordUtil {/*** 生成 word 文档* @param wordTemplatePath    word 模板路径* @param targetWordFilePath  生成目标文档路径* @param data                待渲染的数据模型-哈希表形式*/public static void generateWordFile(String wordTemplatePath, String targetWordFilePath, Map<String, Object> data) {XWPFTemplate template = XWPFTemplate.compile(wordTemplatePath).render(data);try {template.writeAndClose(new FileOutputStream(targetWordFilePath));} catch (IOException e) {e.printStackTrace();}}/*** 生成 word 文档* @param wordTemplatePath    word 模板路径* @param targetWordFilePath  生成目标文档路径* @param data                待渲染的数据模型-Java对象形式*/public static void generateWordFile(String wordTemplatePath, String targetWordFilePath, Object data) {XWPFTemplate template = XWPFTemplate.compile(wordTemplatePath).render(data);try {template.writeAndClose(new FileOutputStream(targetWordFilePath));} catch (IOException e) {e.printStackTrace();}}/*** 生成 word 文档* @param wordTemplatePath    word 模板路径* @param targetWordFilePath  生成目标文档路径* @param data                待渲染的数据模型-哈希表形式* @param configure           渲染配置*/public static void generateWordFile(String wordTemplatePath, String targetWordFilePath, Map<String, Object> data, Configure configure) {XWPFTemplate template = XWPFTemplate.compile(wordTemplatePath, configure).render(data);try {template.writeAndClose(new FileOutputStream(targetWordFilePath));} catch (IOException e) {e.printStackTrace();}}/*** 生成 word 文档* @param wordTemplatePath    word 模板路径* @param targetWordFilePath  生成目标文档路径* @param data                待渲染的数据模型-Java对象形式* @param configure           渲染配置*/public static void generateWordFile(String wordTemplatePath, String targetWordFilePath, Object data, Configure configure) {XWPFTemplate template = XWPFTemplate.compile(wordTemplatePath, configure).render(data);try {template.writeAndClose(new FileOutputStream(targetWordFilePath));} catch (IOException e) {e.printStackTrace();}}
}

最后的最后

希望各位屏幕前的靓仔靓女们给个三连!你轻轻地点了个赞,那将在我的心里世界增添一颗明亮而耀眼的星!

咱们下期再见!

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MySQL——主从复制

简介 在实际的生产中&#xff0c;为了解决Mysql的单点故障已经提高MySQL的整体服务性能&#xff0c;一般都会采用「主从复制」。 主从复制开始前有个前提条件&#xff1a;两边的数据要一样&#xff0c;主必须开启二进制日志 dump thread 线程 基于位置点从是否需要开启二进…

Codeforces Round 895 (Div. 3)

Codeforces Round 895 (Div. 3) A. Two Vessels 思路&#xff1a; 我们可以发现当在 a 拿 c 到 b 其实可以让他们差值减少 2c&#xff0c;所以对a和b的差值除以2c向上取整即可 #include<bits/stdc.h> using namespace std; #define int long long #define rep(i,a,n) …

烟感报警器单片机方案开发,解决方案

烟感报警器也叫做烟雾报警器。烟感报警器适用于火灾发生时有大量烟雾&#xff0c;而正常情况下无烟的场所。例如写字楼、医院、学校、博物馆等场所。烟感报警器一般安装于所需要保护或探测区域的天花板上&#xff0c;因火灾中烟雾比空气轻&#xff0c;更容易向上飘散&#xff0…

如何利用 Selenium 对已打开的浏览器进行爬虫

大家好&#xff01; 在对某些网站进行爬虫时&#xff0c;如果该网站做了限制&#xff0c;必须完成登录才能展示数据&#xff0c;而且只能通过短信验证码才能登录 这时候&#xff0c;我们可以通过一个已经开启的浏览器完成登录&#xff0c;然后利用程序继续操作这个浏览器&…

Redis常见命令

命令可以查看的文档 http://doc.redisfans.com/ https://redis.io/commands/ 官方文档&#xff08;英文&#xff09; http://www.redis.cn/commands.html 中文 https://redis.com.cn/commands.html 个人推荐这个 https://try.redis.io/ redis命令在线测试工具 https://githubfa…

《向量数据库》——向量数据库Milvus 和大模型出联名款AI原生Milvus Cloud

大模型技术的发展正加速对千行百业的改革和重塑,向量数据库作为大模型的海量记忆体、云计算作为大模型的大算力平台,是大模型走向行业的基石。而电商行业因其高度的数字化程度,成为打磨大模型的绝佳“战场”。 在此背景下,Zilliz 联合亚马逊云科技举办的【向量数据库 X 云计…

在FPGA上快速搭建以太网

在本文中&#xff0c;我们将介绍如何在FPGA上快速搭建以太网 &#xff08;LWIP &#xff09;。为此&#xff0c;我们将使用 MicroBlaze 作为主 CPU 运行其应用程序。 LWIP 是使用裸机设计以太网的良好起点&#xff0c;在此基础上我们可以轻松调整软件应用程序以提供更详细的应用…

Chrome 108版(64-bit 108.0.5359.125)网盘下载

还在用Selenium的朋友们注意了&#xff0c;目前Chrome的最新版是116&#xff0c;而官方的Chromedriver只支持到115版。 可惜Google不提供旧版Chrome的下载方式&#xff0c;需要旧版的很难回去了。如果真的想要旧版的Chrome&#xff0c;只能民间自救。 我在2022年12月备份了C盘…

线性代数的学习和整理21,向量的模,矩阵的模,矩阵的模和行列式比较(未完成)

目录 1 模的定义 2 向量的模是距离 2.1 向量的模的定义 2.2 向量的模的计算公式 3 矩阵的模 3.1 矩阵/向量组的模的定义 3.2 矩阵的模的公式 4 矩阵的模和行列式的关系&#xff1f; 1 模的定义 模&#xff0c;又称为范数。范数&#xff0c;是具有“长度”概念的函数。…

机器人任务挖掘与智能超级自动化技术解析

本文为上海财经大学教授、安徽财经大学学术副校长何贤杰出席“会计科技Acctech应对不确定性挑战”高峰论坛时的演讲内容整理。何贤杰详细介绍了机器人任务挖掘与智能超级自动化技术的发展背景、关键技术和应用场景。 从本质来说&#xff0c;会计是非常适合智能化、自动化的。会…

ROS2下使用TurtleBot3-->SLAM导航(仿真)RVIZ加载不出机器人模型

一、问题描述 在使用台式机进行仿真时&#xff0c;大部分例程很顺利&#xff0c;但在SLAM导航时&#xff0c;在RVIZ中却一直加载不出机器人模型&#xff0c;点击Navigation2 Goal选择目标点进行导航时&#xff0c;无响应。 启动后在RVIZ2和终端看到一个错误 按照官网的指令试…

VMware17 下载以及 配置虚拟机 一条龙全教程

前言&#xff1a;在网络安全相关方面的学习时&#xff0c;我们难免要使用虚拟机来模拟实现网络攻击或者防御&#xff0c;在这里&#xff0c;笔者就给大家分享 VMware 17 虚拟机下载安装使用的保姆级教程 目录 虚拟性软件版本推荐 下载 VMware 下载镜像 配置虚拟机 虚拟性软…

MySQL的用户管理

1、MySQL的用户管理 &#xff08;1&#xff09;创建用户 create user zhang3 identified by 123123;表示创建名称为zhang3的用户&#xff0c;密码设为123123。 &#xff08;2&#xff09;了解user表 1&#xff09;查看用户 select host,user,authentication_string,select…

YOLOV7改进-添加基于注意力机制的目标检测头(DYHEAD)

DYHEAD 复制到这&#xff1a; 1、models下新建文件 2、yolo.py中import一下 3、改IDetect这里 4、论文中说6的效果最好&#xff0c;但参数量不少&#xff0c;做一下工作量 5、在进入IDetect之前&#xff0c;会对RepConv做卷积 5、因为DYHEAD需要三个层输入的特征层一致&am…

借助各大模型的优点生成原创视频(真人人声)Plus

【技术背景】 众所周知&#xff0c;组成视频的3大元素&#xff0c;即文本语音图片。接着小编逐一介绍生成原创视频的过程。 【文本生成】 天工AI搜索&#xff08;thttp://iangong.cn&#xff09; 直接手机短信验证就可以使用&#xff0c;该大模型已经接入互联网&#xff0c…

工业4.0时代生产系统对接集成优势,MES和ERP专业一体化管理-亿发

在现代制造业中&#xff0c;市场变化都在不断加速。企业面临着不断加强生产效率、生产质量和快速适应市场需求的挑战。在制造行业&#xff0c;日常管理中的ERP系统、MES系统就显得尤为重要。越来越多的企业正在采用MES系统和ERP管理系统的融合&#xff0c;以实现智能化生产管理…

OPENCV实现图像查找

特征匹配+单应性矩阵 # -*- coding:utf-8 -*- """ 作者:794919561 日期:2023/9/4 """ import cv2 import numpy as np# 读图像 img1 = cv2.imread(F:\\learnOpenCV\\openCVLearning\\pictures\\chess

若依前端vue设置子路径

若依前端vue设置子路径 说明&#xff1a;本文档中以前后端分离版为例&#xff0c;版本为:3.8.6 一设置变量 在.env.development和.env.production 中定义一个变量如VUE_APP_PROJECT_IDENTIFIER # 项目标识字符 VUE_APP_PROJECT_IDENTIFIER admin二引用路径变量 ${process…

自动化测试的重要性:为何追求自动化?

为什么需要自动化测试&#xff1f; 代替手工重复操作&#xff0c;测试工程师可以花更多时间在设计全面的测试用例和新功能测试上 【代替手工重复】 提升回归测试的效率&#xff0c;适合敏捷开发过程 【提升回归效率】 更好的利用非工作时间执行测试&#xff0c;工作时间分析失…

C++项目实战——基于多设计模式下的同步异步日志系统-②-相关技术补充(不定参函数)

文章目录 专栏导读不定参函数C风格不定参函数不定参宏函数 专栏导读 &#x1f338;作者简介&#xff1a;花想云 &#xff0c;在读本科生一枚&#xff0c;C/C领域新星创作者&#xff0c;新星计划导师&#xff0c;阿里云专家博主&#xff0c;CSDN内容合伙人…致力于 C/C、Linux 学…