乐趣区

关于后端:Spring-Boot-中使用-Poitl-渲染数据并生成-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 {

    @Test
    public 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 渲染多个段落和表格
TOCRenderPolicy Beta 试验性能:目录,打开文档时须要更新域

应用插件

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

当咱们有个模板标签为 {{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 {

    @Test
    public 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
}

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

    @Test
    public 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();
        }
    }
}

最初的最初

心愿各位屏幕前的 靓仔靓女们 给个三连!你轻轻地点了个赞,那将在我的心里世界削减一颗亮堂而夺目的星!

咱们下期再见!

退出移动版