<article class=“article fmt article-content”><blockquote>本文 Demo 已收录到 demo-for-all-in-java 我的项目中,欢送大家 <code>star</code> 反对!后续将继续更新!</blockquote><h2>前言</h2><blockquote>产品经理急冲冲地走了过去。「当初须要将按这些数据生成一个 Word 报告文档,你来安顿下」</blockquote><p>我的项目中有这么一个需要,须要将用户填写的数据填充到一个 Word 文档中,而这个 Word 文档是人家给定了的。<strong>换句话说,让你依照这个文档的内容格局生成新的文档。</strong></p><h2>什么是 Poi-tl ?</h2><blockquote>官网:http://deepoove.com/poi-tl/1.9.x/</blockquote><p><code>poi-tl</code>(poi template language)是一种 Word 模板引擎,能够基于 Word 模板和数据生成新的文档,它的底层是通过 Apache POI 来实现的。</p><p>Apache POI 不仅封装了易用的文档 API (文本、图片、表格、页眉、页脚、图表等),也能够在底层间接操作文档XML构造。</p><p><code>poi-tl</code> 领有如下个性(<strong>理解瞄一眼就行</strong>):</p><table><thead><tr><th>内容</th><th>形容</th></tr></thead><tbody><tr><td>文本</td><td>将标签渲染为文本</td></tr><tr><td>图片</td><td>将标签渲染为图片</td></tr><tr><td>表格</td><td>将标签渲染为表格</td></tr><tr><td>列表</td><td>将标签渲染为列表</td></tr><tr><td>图表</td><td>条形图(3D条形图)、柱形图(3D柱形图)、面积图(3D面积图)、折线图(3D折线图)、雷达图、饼图(3D饼图)等图表渲染</td></tr><tr><td>If Condition判断</td><td>暗藏或者显示某些文档内容(包含文本、段落、图片、表格、列表、图表等)</td></tr><tr><td>Foreach Loop循环</td><td>循环某些文档内容(包含文本、段落、图片、表格、列表、图表等)</td></tr><tr><td>Loop表格行</td><td>循环渲染表格的某一行</td></tr><tr><td>Loop表格列</td><td>循环渲染表格的某一列</td></tr><tr><td>Loop有序列表</td><td>反对有序列表的循环,同时反对多级列表</td></tr><tr><td>图片替换</td><td>将原有图片替换成另一张图片</td></tr><tr><td>书签、锚点、超链接</td><td>反对设置书签,文档内锚点和超链接性能</td></tr><tr><td>弱小的表达式</td><td>齐全反对SpringEL表达式,能够扩大更多的表达式:OGNL, MVEL…</td></tr><tr><td>标签定制</td><td>反对自定义标签前后缀</td></tr><tr><td>文本框</td><td>文本框内标签反对</td></tr><tr><td>款式</td><td>模板即款式,同时代码也能够设置款式</td></tr><tr><td>模板嵌套</td><td>模板蕴含子模板,子模板再蕴含子模板</td></tr><tr><td>合并</td><td>Word合并Merge,也能够在指定地位进行合并</td></tr><tr><td>用户自定义函数(插件)</td><td>在文档任何地位执行函数</td></tr></tbody></table><p>咱们就能够应用这个它来实现这个需要。</p><h2>如何应用 Poi-tl ?</h2><p>本篇文章将以 Spring Boot 我的项目作为演示,屏幕前的敌人们能够一起跟着我的步骤来,实际一番!</p><ol><li><strong>首先创立一个 Spring Boot 我的项目,版本目前我的 Demo 是 2.2.1,你能够更改你的 Spring Boot 版本,那当初我这里曾经创立好了。</strong></li></ol><p>其中, <code>pom.xml</code> 只有两个依赖项,一个 web 和一个 test :</p><pre><code class=“xml”><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></code></pre><ol start=“2”><li><strong>接着在 <code>pom.xml</code> 中引入 Poi-tl 的依赖项</strong>:</li></ol><pre><code class=“xml”><!– Poi-tl Word 模板引擎–><dependency> <groupId>com.deepoove</groupId> <artifactId>poi-tl</artifactId> <version>1.9.1</version></dependency></code></pre><ol start=“3”><li><strong>筹备一个 Word 模板</strong></li></ol><p>这一步你能够本人动手做一个 Word 模板,这里我先演示下。就先创立一个名为 <code>Hello World.docx</code> 的 Word 文档,模板内容如下:</p><p></p><p>找一个你喜爱的地位寄存这个模板,我当初把它放到我的项目的 <code>resource</code> 目录。</p><blockquote><code>{{title}}</code> 这种由两个大括号包住的,目前能够看成占位符,这个模板中有 4 个占位符,后续的数据就渲染到这些中央上。</blockquote><ol start=“4”><li><strong>获取模板所在的门路,并将数据渲染到模板上</strong></li></ol><p>渲染只需一行代码,就是应用 <code>XWPFTemplate</code> 的 API 就能够了,通过 <code>complie</code> 和 <code>render</code> 办法,就能够将数据渲染到模板中,失去渲染好的新文档。</p><pre><code class=“java”>@SpringBootTestpublic 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(); } } }</code></pre><p>执行这个单元测试,就能够看到在我的项目所在目录下输入了新的文档 <code>output.docx</code>,关上这个文档,咱们就能够看到如下图所示的内容:</p><p></p><p><strong>功败垂成,这就是渲染好的新文档了,是不是很简略,一行代码就实现了依据模板进行数据的渲染!</strong></p><h2>相干概念</h2><h3>模板</h3><p>模板是 Docx 格局的 Word 文档,咱们能够应用 Microsoft office、WPS Office 等软件来制作模板。</p><h3>标签</h3><p>下面说到 <code>{{title}}</code> 这种了解成占位符,实际上,官网是称之为「<strong>标签</strong>」。</p><p>所有的标签都是以 <code>{{</code> 结尾,以 <code>}}</code> 结尾,标签能够呈现在任何地位,包含页眉,页脚,表格外部,文本框等。</p><blockquote>表格布局能够设计出很多优良业余的文档,举荐应用表格布局。</blockquote><p>poi-tl 模板遵循 <code>所见即所得</code> 的设计,模板和标签的款式会被齐全保留,就如我下面演示的,<strong>一级题目和字体色彩的款式就被保留下来了。</strong></p><h3>数据模型</h3><p>数据模型,也就是咱们须要渲染到模板中的数据,能够是哈希表,也能够是一般的 Java 对象。</p><ol><li>哈希表(key 名是标签名):</li></ol><pre><code class=“java”>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 ?再不关注我可要求你关注了!”);</code></pre><ol start=“2”><li>Java 对象(属性名是标签名):</li></ol><pre><code class=“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 ?再不关注我可要求你关注了!”);</code></pre><p>有了哈希表和或者 Java 对象的数据模型后,<strong>将这个数据丢给渲染的 API</strong>,就能够实现数据的渲染了。</p><h2>标签的写法</h2><p>poi-tl 里只有标签,那么咱们须要晓得标签的写法是怎么的。在 Word 文档里,能够有:文本、图片、表格、列表等元素,那么对应的,咱们的标签也有这些。</p><h3>文本标签 {{var}}</h3><p>简略粗犷,间接 <code>{{标签名}}</code> 就是文本标签了。</p><h3>图片标签 {{@var}}</h3><p><code>{{@标签名}}</code> 就是图片标签,<code>@</code> 标识了这个标签的类型是图片,其余的也是同理,不同的符号标识不同类型的标签。</p><h3>表格标签 {{#var}}</h3><p>应用 <code>#</code> 标识这是一个表格标签。</p><h3>列表标签 {{var}}</h3><p>应用 <code></code> 标识这是一个列表标签。</p><h3>其余的标签</h3><p>剩下的标签还有很多,具体的内容你能够浏览官网文档,这里就不一一介绍了。</p><p>上面我将写下我用过的内容。</p><h2>插件</h2><p>插件,又称为<strong>自定义函数</strong>,它容许咱们在模板标签地位处执行事后定义好的函数。因为插件机制的存在,咱们简直能够在模板的任何地位执行任意操作。</p><p><strong>插件是 poi-tl 的外围</strong>,默认的标签和援用标签都是通过插件加载。</p><h3>默认插件</h3><p>poi-tl 默认提供了八个策略插件,用来解决文本、图片、列表、表格、文档嵌套、援用图片、援用多系列图表、援用单系列图表等:</p><ul><li>TextRenderPolicy</li><li>PictureRenderPolicy</li><li>NumberingRenderPolicy</li><li>TableRenderPolicy</li><li>DocxRenderPolicy</li><li>MultiSeriesChartTemplateRenderPolicy</li><li>SingleSeriesChartTemplateRenderPolicy</li><li>DefaultPictureTemplateRenderPolicy</li></ul><p>因为这 8 个插件是常常用到的,所以这些插件被注册为不同的标签类型,也就是咱们看到过的 <code>{{var}}、{{@var}}、{{#var}}</code> 等不同类型的标签,从而搭建了 poi-tl 的标签体系。</p><p>除了这 8 个通用的策略插件外,还内置了一些额定用处的插件:</p><table><thead><tr><th><code>DynamicTableRenderPolicy</code></th><th>动静表格插件,容许间接操作表格对象</th><th>示例-动静表格</th></tr></thead><tbody><tr><td><code>HackLoopTableRenderPolicy</code></td><td>循环表格行,下文会具体介绍</td><td>示例-表格行循环</td></tr><tr><td><code>LoopColumnTableRenderPolicy</code></td><td>循环表格列</td><td>示例-表格列循环</td></tr><tr><td><code>BookmarkRenderPolicy</code></td><td>书签和锚点</td><td>示例-Swagger文档</td></tr><tr><td><code>JSONRenderPolicy</code></td><td>高亮显示JSON代码块</td><td>示例-Swagger文档</td></tr><tr><td><code>AbstractChartTemplateRenderPolicy</code></td><td>援用图表插件,容许间接操作图表对象</td><td> </td></tr><tr><td><code>ParagraphRenderPolicy</code></td><td>渲染一个段落,能够蕴含不同款式文本,图片等</td><td> </td></tr><tr><td><code>DocumentRenderPolicy</code></td><td>渲染多个段落和表格</td><td> </td></tr><tr><td><code>TOCRenderPolicy</code></td><td>Beta试验性能:目录,打开文档时须要更新域</td><td> </td></tr></tbody></table><h3>应用插件</h3><p>为了让插件在某个标签处执行,咱们须要将<strong>插件与标签绑定</strong>。</p><p>当咱们有个模板标签为 <code>{{description}}</code>,默认是文本标签,如果心愿在这个地位做些不一样或者更简单的事件,咱们能够<strong>将插件利用到这个模板标签</strong>,比方渲染 HTML:</p><pre><code class=“java”>ConfigureBuilder builder = Configure.builder();builder.bind(“description”, new HtmlRenderPolicy());</code></pre><p><strong>此时,<code>{{description}}</code> 将不再是一个文本标签,而是一个自定义的反对 HTML 渲染的标签。</strong></p><p>当然,这里的 HTML 渲染的插件,默认是没有提供的,须要引入以下的依赖项,能力反对 HTML 的渲染。</p><pre><code class=“xml”><!– 反对渲染 HTML 的插件 –><dependency> <groupId>io.github.draco1023</groupId> <artifactId>poi-tl-ext</artifactId> <version>0.3.3</version></dependency></code></pre><p>示例:咱们对 {{author}} 这个标签绑定上反对 HTML 渲染的插件,这样就能渲染 HTML 的文本了。</p><pre><code class=“java”>@SpringBootTestpublic 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(); } } }</code></pre><p>生成的 Word 文档如下图所示,能够看到 {{author}} 这个标签的 HTML 文本曾经渲染胜利了!(蓝框框)</p><p></p><h2>表格行循环</h2><p>当有相似如下需要的时候,在表格里展现多行同类型的数据,那么就须要用到表格的行循环了。</p><p></p><p>这里也是波及到插件的,具体就是应用 <code>HackLoopTableRenderPolicy</code> 这个插件策略,这个策略可能依据汇合数据进行循环渲染,这样就渲染数据到表格的行上了,汇合有多少个元素,那么就有多少行。</p><p>咱们来看下这个表格行循环的模板是怎么写的,是这样的:</p><p></p><p>能够看到 <code>{{articles}}</code> 和 <code>{{columns}}</code> 是规范的文本标签,咱们这里将这两个标签置于<strong>循环行的上一行</strong>,循环行里设置要循环的标签和内容,留神这里的标签是应用 <code>[]</code> 的,以此来辨别规范的标签语法。</p><p><strong>同时 <code>{{articles}}</code> 和 <code>{{columns}}</code> 标签对应的数据就是文章和专栏的汇合。</strong></p><p>咱们写一个该模板的数据模型,以 Java 对象来写,同时模仿数据从数据库中读取。</p><p><strong>数据模型:AcWordModel</strong></p><pre><code class=“java”>public class AcWordModel { /** * 文章明细数据模型-表格行循环 / private List<Article> articles; /* * 专栏明细数据模型 / private List<SpecialColumn> columns; // 省略 getter 和 setter}</code></pre><p>其中的 Article 和 SpecialColumn 模型如下:</p><pre><code class=“java”>public class Article { private String title; private String tags; private Integer reading; private Integer likes; // 省略 getter 和 setter}</code></pre><pre><code class=“java”>public class SpecialColumn { private String name; private Integer subscription; private Integer nums; // 省略 getter 和 setter}</code></pre><p>进行测试,获取数据和模板,让标签和表格行循环的插件进行绑定</p><pre><code class=“java”> @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(); } }</code></pre><p>这样,就能实现表格的行循环了!</p><h2>封装 Word 渲染生成新文档的工具</h2><p>咱们能够再封装下这个 API,写一个工具类,如下:</p><pre><code class=“java”>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(); } }}</code></pre><h2>最初的最初</h2><p><strong>心愿各位屏幕前的</strong><code>靓仔靓女们</code><strong>给个三连!你轻轻地点了个赞,那将在我的心里世界削减一颗亮堂而夺目的星!</strong></p><p><strong>咱们下期再见!</strong></p></article>