poi-tl实现对Word模板中复杂表格的数据填充


前言

开发时, 我们有时需要进行word类型表格导出,
而对于表格操作. 我们一般可能会倾向于使用 poi 进行操作. 但poi操作比较复杂,
所以就在寻找一种可以快速将内容填充到表格中的工具. 而pot-tl 恰好满足了我们这一需求.

what poi-tl

poi-tl(poi template language)是Word模板引擎,使用Word模板和数据创建很棒的Word文档.
在文档的任何地方做任何事情(Do Anything Anywhere)是poi-tl的星辰大海. 官方文档

why poi-tl

方案 移植性 功能性 易用性
Poi-tl Java跨平台 Word模板引擎,基于Apache POI,提供更友好的API 低代码,准备文档模板和数据即可
Apache POI Java跨平台 Apache项目,封装了常见的文档操作,也可以操作底层XML结构 文档不全,这里有一个教程:Apache POI Word快速入门
Freemarker XML跨平台 仅支持文本,很大的局限性 不推荐,XML结构的代码几乎无法维护
OpenOffice 部署OpenOffice,移植性较差 - 需要了解OpenOffice的API
HTML浏览器导出 依赖浏览器的实现,移植性较差 HTML不能很好的兼容Word的格式,样式糟糕 -
Jacob、winlib Windows平台 - 复杂,完全不推荐使用

poi-tl是一个基于Apache POI的Word模板引擎,也是一个免费开源的Java类库,你可以非常方便的加入到你的项目中,并且拥有着让人喜悦的特性.

Word模板引擎功能 描述
文本 将标签渲染为文本
图片 将标签渲染为图片
表格 将标签渲染为表格
列表 将标签渲染为列表
图表 条形图(3D条形图)、柱形图(3D柱形图)、面积图(3D面积图)、折线图(3D折线图)、雷达图、饼图(3D饼图)、散点图等图表渲染
If Condition判断 根据条件隐藏或者显示某些文档内容(包括文本、段落、图片、表格、列表、图表等)
Foreach Loop循环 根据集合循环某些文档内容(包括文本、段落、图片、表格、列表、图表等)
Loop表格行 循环复制渲染表格的某一行
Loop表格列 循环复制渲染表格的某一列
Loop有序列表 支持有序列表的循环,同时支持多级列表
Highlight代码高亮 word中代码块高亮展示,支持26种语言和上百种着色样式
Markdown 将Markdown渲染为word文档
Word批注 完整的批注功能,创建批注、修改批注等
Word附件 Word中插入附件
SDT内容控件 内容控件内标签支持
Textbox文本框 文本框内标签支持
图片替换 将原有图片替换成另一张图片
书签、锚点、超链接 支持设置书签,文档内锚点和超链接功能
Expression Language 完全支持SpringEL表达式,可以扩展更多的表达式:OGNL, MVEL…
样式 模板即样式,同时代码也可以设置样式
模板嵌套 模板包含子模板,子模板再包含子模板
合并 Word合并Merge,也可以在指定位置进行合并
用户自定义函数(插件) 插件化设计,在文档任何位置执行函数

注意: 只能操作.docx格式的word,不能操作.doc格式的word. 只能操作word中的表格, 不能操作Excel中的表格

How poi-tl

1. 版本问题

在使用poi-tl时, 需要注意版本之间的冲突问题. 下面我们将使用1.10.x版本, 因此其他环境为: jdk1.8, poi:4.1.2

V1.12.0版本作了一个不兼容的改动,升级的时候需要注意:

  • 重构了PictureRenderData,改为抽象类,建议使用Pictures工厂方法来创建图片数据

2. 集成和使用

2.1 pom文件坐标

<!-- POI -->		
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi</artifactId>
    <version>4.1.2</version>
</dependency>
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>4.1.2</version>
</dependency>
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml-schemas</artifactId>
    <version>4.1.2</version>
</dependency>

<!-- poi-tl -->
<dependency>
    <groupId>com.deepoove</groupId>
    <artifactId>poi-tl</artifactId>
    <version>1.10.0</version>
</dependency>

2.2 测试代码-map方式(最简单实用)
复杂表格中, 可以使用这种方式进行依次填充

@Test
public void TestPoiTi() throws IOException {
    
    
    //===================使用Map的方式================================
    //创建目标文件C:\Users\Administrator\Documents
    Resource resource = new ClassPathResource("static/" + "poi_ti_test.docx");
    File sourceFile = resource.getFile();
    //构建数据
    Map<String, Object> data = new HashMap();
    data.put("name", "江江");
    data.put("dept", "重机部");
    data.put("level", "工程师");
    data.put("leader", "思必达");
    //创建输出流
    OutputStream os = new FileOutputStream("template1_out.docx");
    //最终编译渲染并输出
    XWPFTemplate.compile(sourceFile).render(data).writeAndClose(os);
    System.out.println("输出完毕");

}

创建表格模板

填充效果

2.3 行循环的形式
需要在Configure对象中绑定需要循环的list对象

//创建行循环策略
LoopRowTableRenderPolicy rowTableRenderPolicy = new LoopRowTableRenderPolicy();
//告诉模板引擎,要在employees做行循环,绑定行循环策略
Configure configure = Configure.builder().bind("employees", rowTableRenderPolicy).build();
//创建目标文件
Resource resource = new ClassPathResource("static/" + "poi_ti_test2.docx");
File sourceFile = resource.getFile();
//构建数据
Map<String, Object> data = new HashMap();

//1.学生数据
List<Employee> employees = new ArrayList<>();
Employee e = new Employee("江江", "重机部", "工程师", "思必达");
Employee e2 = new Employee("江江2", "重机部2", "工程师2", "思必达2");
employees.add(e);
employees.add(e2);
//2.设置到students字段中
data.put("employees", employees);
//创建输出流
OutputStream os = new FileOutputStream("template2_out.docx");
//最终编译渲染并输出
XWPFTemplate.compile(sourceFile, configure).render(data).writeAndClose(os);

创建表格模板

填充效果
image-20221205141122494

2.4 通过模板写入, 并通过浏览器或请求返回

public void downloadDispatchList(Integer carReserveId, HttpServletResponse response) throws IOException {
    
    
    //用于填充的数据体
    CarReserveVO carReserveVO = getCarReserveVOById(carReserveId);
    org.springframework.core.io.Resource resource = new ClassPathResource("static/" + "模板文件.docx");
    File sourceFile = resource.getFile();
    //在模板文件中任意表格位置填充数据
    Map<String, Object> data = new HashMap();
    data.put("ycbm", carReserveVO.getCarUserDept());
    data.put("ycr", carReserveVO.getCarUserName());
    data.put("start_time", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(carReserveVO.getBookerStartTime()));
    data.put("end_time", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(carReserveVO.getBookerEndTime()));
 
    //浏览器端下载
    response.setCharacterEncoding("utf-8");
    response.setContentType("application/msword");
    String fileName = carReserveVO.getCarUserName() + "的用车申请单" + ".docx";
    response.setHeader("Content-Disposition", "attachment;filename="
            .concat(String.valueOf(URLEncoder.encode(fileName, "UTF-8"))));
    response.flushBuffer();
    BufferedInputStream bis = new BufferedInputStream(new FileInputStream(sourceFile));
    //创建输出流
    OutputStream os = response.getOutputStream();
    //最终编译渲染并输出
    XWPFTemplate.compile(sourceFile).render(data).writeAndClose(os);
    byte[] buffer = new byte[1024];
    int i = bis.read(buffer);
    while (i != -1) {
    
    
        os.write(buffer, 0, i);
        i = bis.read(buffer);
    }
    if (bis != null) {
    
    
        bis.close();
    }
}

表格模板
在这里插入图片描述
填充效果
在这里插入图片描述

3. SpringEL表达式

Spring Expression Language 是一个强大的表达式语言,支持在运行时查询和操作对象图,可作为独立组件使用,也可作为poi-tl模板上, 用于模板填充时参数的引用. 单独使用时需要引入相应的依赖:

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-expression</artifactId>
  <version>5.3.18</version>
</dependency>

关于SpringEL的写法可以参见官方文档,下面给出一些典型的示例

{
   
   {name}}
{
   
   {name.toUpperCase()}} 		类方法调用,转大写
{
   
   {name == 'poi-tl'}} 		判断条件
{
   
   {empty?:'这个字段为空'}}	
{
   
   {sex ? '男' : '女'}}   	   三目运算符
{
   
   {new java.text.SimpleDateFormat('yyyy-MM-dd HH:mm:ss').format(time)}}  类方法调用,时间格式化
{
   
   {price/10000 + '万元'}} 		运算符
{
   
   {dogs[0].name}} 			 数组列表使用下标访问
{
   
   {localDate.format(T(java.time.format.DateTimeFormatter).ofPattern('yyyy年MM月dd日'))}}  使用静态类方法

总结

根据poi-tl 可以操作含有多种类型的复杂 Word 文档, 包括:文本, 表格, 图片, 附件. markdown等.
并且支持表格行循环, 表格列循环, 动态表格, 批注, 附件, 高亮等等.
更多使用方式可以参照 官方文档, 或者 C站某大佬的一篇使用教程,
后续如有其他需求我也将在本文进行持续更新. 下次见~

猜你喜欢

转载自blog.csdn.net/qq_43371556/article/details/128180988