我们在开发一些系统的时候,例如OA系统,经常能遇到将审批单数据导出为word和excel文档的需求,导出为excel是比较简单的,因为excel有单元格来供我们定位数据位置,但是word文档的格式不像表格那样可以轻松的定位,要想将数据导出为一些带有图片和表格的这种结构复杂的word文档该怎样实现呢。
poi-tl ^[1]^是一款可以帮助我们实现这种功能的Java开源项目,它把POI和Freemarker相结合,可以基于我们绘制好的word文档模板来填充数据进去,然后生成新的word文档。poi-tl托管在GitHub:https://github.com/Sayi/poi-tl{#fnref:1}
例如,我们要生成一个差旅行程单,首先要绘制这样的一个word文档模板,用{{name}}
代表姓名进行占位,姓名就是普通文字类型,以此类推。tripList
作为渲染行程表格的数据源的名字,是ArrayList集合类型,放在表格的表头,用[from]
表示tripList
集合中每个元素的from
属性的值,渲染到当前行的某一列上,以此类推。最后的三个签署对应的是领导的签名笔迹图片,图片类型要用变量名前多一个@
的形式{{@****Pin}}
来表示
如果表格中某一列是图片,则表示为
[@变量]
模板绘制好以后,开始使用poi-tl工具生成word文档,首先新建maven项目,引入poi-tl的依赖和需要的其他依赖,然后将这个绘制好的word模板文件放在工程的根目录下
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>groupId</span><span class="token punctuation">></span></span>org.example<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>groupId</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>artifactId</span><span class="token punctuation">></span></span>poi-tl<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>artifactId</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>version</span><span class="token punctuation">></span></span>1.0-SNAPSHOT<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>version</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>properties</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>maven.compiler.source</span><span class="token punctuation">></span></span>8<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>maven.compiler.source</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>maven.compiler.target</span><span class="token punctuation">></span></span>8<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>maven.compiler.target</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>project.build.sourceEncoding</span><span class="token punctuation">></span></span>UTF-8<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>project.build.sourceEncoding</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>properties</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dependencies</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dependency</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>groupId</span><span class="token punctuation">></span></span>com.deepoove<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>groupId</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>artifactId</span><span class="token punctuation">></span></span>poi-tl<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>artifactId</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>version</span><span class="token punctuation">></span></span>1.12.2<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>version</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dependency</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dependency</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>groupId</span><span class="token punctuation">></span></span>org.projectlombok<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>groupId</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>artifactId</span><span class="token punctuation">></span></span>lombok<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>artifactId</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>version</span><span class="token punctuation">></span></span>1.18.24<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>version</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>scope</span><span class="token punctuation">></span></span>provided<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>scope</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dependency</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dependencies</span><span class="token punctuation">></span></span>
</project>
然后,新建一个Entity类: org.example.TravelApplyExportVO
package org.example;
import com.deepoove.poi.data.PictureRenderData;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.ArrayList;
import java.util.List;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class TravelApplyExportVO {
private String no;
private String name;
private String dept;
private String employeeNo;
private String start;
private String end;
private String days;
private String address;
private String reason;
/**
* com.deepoove.poi.data.PictureRenderData 代表图片
*/
private PictureRenderData applyPin;
private PictureRenderData bossPin;
private PictureRenderData leaderPin;
<span class="token keyword">private</span> <span class="token class-name">String</span> date<span class="token punctuation">;</span>
<span class="token comment">/**
* 用于渲染表格的集合
*/</span>
<span class="token keyword">private</span> <span class="token class-name">List</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">Route</span><span class="token punctuation">></span></span> tripList <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ArrayList</span><span class="token generics"><span class="token punctuation"><</span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token annotation punctuation">@Data</span>
<span class="token annotation punctuation">@AllArgsConstructor</span>
<span class="token annotation punctuation">@NoArgsConstructor</span>
<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">class</span> <span class="token class-name">Route</span> <span class="token punctuation">{</span>
<span class="token keyword">private</span> <span class="token class-name">String</span> from<span class="token punctuation">;</span>
<span class="token keyword">private</span> <span class="token class-name">String</span> <span class="token keyword">to</span><span class="token punctuation">;</span>
<span class="token keyword">private</span> <span class="token class-name">String</span> flight<span class="token punctuation">;</span>
<span class="token keyword">private</span> <span class="token class-name">String</span> depTime<span class="token punctuation">;</span>
<span class="token keyword">private</span> <span class="token class-name">String</span> arrTime<span class="token punctuation">;</span>
<span class="token keyword">private</span> <span class="token class-name">String</span> cabin<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
}
新建测试类: org.example.Main,用poi-tl组件基于刚刚绘制的word模板生成一个差旅行程单
package org.example;
import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import com.deepoove.poi.data.PictureRenderData;
import com.deepoove.poi.data.Pictures;
import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy;
import lombok.SneakyThrows;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
public class Main {
<span class="token annotation punctuation">@SneakyThrows</span>
<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token class-name">TravelApplyExportVO</span> vo <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">TravelApplyExportVO</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
vo<span class="token punctuation">.</span><span class="token function">setNo</span><span class="token punctuation">(</span><span class="token string">"202500001"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
vo<span class="token punctuation">.</span><span class="token function">setName</span><span class="token punctuation">(</span><span class="token string">"lzj"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
vo<span class="token punctuation">.</span><span class="token function">setDept</span><span class="token punctuation">(</span><span class="token string">"技术部"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
vo<span class="token punctuation">.</span><span class="token function">setEmployeeNo</span><span class="token punctuation">(</span><span class="token string">"00000001"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
vo<span class="token punctuation">.</span><span class="token function">setStart</span><span class="token punctuation">(</span><span class="token string">"2025-01-01"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
vo<span class="token punctuation">.</span><span class="token function">setEnd</span><span class="token punctuation">(</span><span class="token string">"2025-02-01"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
vo<span class="token punctuation">.</span><span class="token function">setDays</span><span class="token punctuation">(</span><span class="token string">"30"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
vo<span class="token punctuation">.</span><span class="token function">setAddress</span><span class="token punctuation">(</span><span class="token string">"中国香港"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
vo<span class="token punctuation">.</span><span class="token function">setReason</span><span class="token punctuation">(</span><span class="token string">"系统维护"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 在项目根路径读取笔迹图片,并设置大小</span>
<span class="token class-name">PictureRenderData</span> data1 <span class="token operator">=</span> <span class="token class-name">Pictures</span><span class="token punctuation">.</span><span class="token function">ofBytes</span><span class="token punctuation">(</span><span class="token class-name">Files</span><span class="token punctuation">.</span><span class="token function">readAllBytes</span><span class="token punctuation">(</span><span class="token class-name">Paths</span><span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">"img2.png"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">size</span><span class="token punctuation">(</span><span class="token number">120</span><span class="token punctuation">,</span> <span class="token number">60</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name">PictureRenderData</span> data2 <span class="token operator">=</span> <span class="token class-name">Pictures</span><span class="token punctuation">.</span><span class="token function">ofBytes</span><span class="token punctuation">(</span><span class="token class-name">Files</span><span class="token punctuation">.</span><span class="token function">readAllBytes</span><span class="token punctuation">(</span><span class="token class-name">Paths</span><span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">"img.png"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">size</span><span class="token punctuation">(</span><span class="token number">120</span><span class="token punctuation">,</span> <span class="token number">60</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name">PictureRenderData</span> data3 <span class="token operator">=</span> <span class="token class-name">Pictures</span><span class="token punctuation">.</span><span class="token function">ofBytes</span><span class="token punctuation">(</span><span class="token class-name">Files</span><span class="token punctuation">.</span><span class="token function">readAllBytes</span><span class="token punctuation">(</span><span class="token class-name">Paths</span><span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">"img.png"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">size</span><span class="token punctuation">(</span><span class="token number">120</span><span class="token punctuation">,</span> <span class="token number">60</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
vo<span class="token punctuation">.</span><span class="token function">setApplyPin</span><span class="token punctuation">(</span>data1 <span class="token punctuation">)</span><span class="token punctuation">;</span>
vo<span class="token punctuation">.</span><span class="token function">setBossPin</span><span class="token punctuation">(</span> data2<span class="token punctuation">)</span><span class="token punctuation">;</span>
vo<span class="token punctuation">.</span><span class="token function">setLeaderPin</span><span class="token punctuation">(</span> data3<span class="token punctuation">)</span><span class="token punctuation">;</span>
vo<span class="token punctuation">.</span><span class="token function">setDate</span><span class="token punctuation">(</span><span class="token string">"2025-01-10"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 行程List,最终渲染到文档的表格中</span>
vo<span class="token punctuation">.</span><span class="token function">setTripList</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">ArrayList</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">TravelApplyExportVO<span class="token punctuation">.</span>Route</span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token punctuation">{</span>
<span class="token function">add</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">TravelApplyExportVO<span class="token punctuation">.</span>Route</span><span class="token punctuation">(</span><span class="token string">"BJX"</span><span class="token punctuation">,</span><span class="token string">"ZQZ"</span><span class="token punctuation">,</span><span class="token string">"ZH5643"</span><span class="token punctuation">,</span><span class="token string">"2025-01-01 15:00"</span><span class="token punctuation">,</span><span class="token string">"2025-01-01 16:00"</span><span class="token punctuation">,</span><span class="token string">"E"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">add</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">TravelApplyExportVO<span class="token punctuation">.</span>Route</span><span class="token punctuation">(</span><span class="token string">"ZQZ"</span><span class="token punctuation">,</span><span class="token string">"CDE"</span><span class="token punctuation">,</span><span class="token string">"JUH6532"</span><span class="token punctuation">,</span><span class="token string">"2026-01-01 15:00"</span><span class="token punctuation">,</span><span class="token string">"2025-12-01 16:00"</span><span class="token punctuation">,</span><span class="token string">"A"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">add</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">TravelApplyExportVO<span class="token punctuation">.</span>Route</span><span class="token punctuation">(</span><span class="token string">"BJX"</span><span class="token punctuation">,</span><span class="token string">"ZQZ"</span><span class="token punctuation">,</span><span class="token string">"KJU0954"</span><span class="token punctuation">,</span><span class="token string">"2027-01-01 15:00"</span><span class="token punctuation">,</span><span class="token string">"2025-05-01 16:00"</span><span class="token punctuation">,</span><span class="token string">"Q"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name">LoopRowTableRenderPolicy</span> policy <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">LoopRowTableRenderPolicy</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// !!将tripList通过表格来渲染</span>
<span class="token class-name">Configure</span> config <span class="token operator">=</span> <span class="token class-name">Configure</span><span class="token punctuation">.</span><span class="token function">builder</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span><span class="token string">"tripList"</span><span class="token punctuation">,</span> policy<span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name">XWPFTemplate</span> template <span class="token operator">=</span> <span class="token class-name">XWPFTemplate</span>
<span class="token punctuation">.</span><span class="token function">compile</span><span class="token punctuation">(</span><span class="token string">"模板.docx"</span><span class="token punctuation">,</span> config<span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">render</span><span class="token punctuation">(</span>vo<span class="token punctuation">)</span><span class="token punctuation">;</span>
template<span class="token punctuation">.</span><span class="token function">writeAndClose</span><span class="token punctuation">(</span><span class="token class-name">Files</span><span class="token punctuation">.</span><span class="token function">newOutputStream</span><span class="token punctuation">(</span><span class="token class-name">Paths</span><span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">"output.docx"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
}
然后领导签名笔记图片素材img.png
,img2.png
也需要放进工程根目录下
都完成后,执行main()
方法测试,程序运行结束后,将在根路径生成文件output.docx,打开就是我们想要的效果了。
文章写到这已经是深夜了,最新在开发一个OA系统,需要Word导出的场景很多,由于之前没怎么接触过,于是熬夜攻关了一下并记录了下来,接下来希望项目能顺利交付吧~
参考
- https://deepoove.com/poi-tl/ ↩{#fn:1}