文章部分内容来自 Poi-tl Documentation (deepoove.com)
生成word文件的方式有很多种,诸如apache-poi,freemaker,我们都知道word文件其实本质就是一个xml格式的文件,所以市面上大部分配置都是xml的或者是html格式的,对于开发者而言维护、学习成本较高,所以有没有一种更加简单,配置对于开发者更友好的方式呢?
现在我给大家推荐一种:POI-TL,poi大家都很熟悉就是apache的poi,用来做office组件是比较通用的做法,今天介绍的poi-tl是一种使用模板和数据就能创建很棒的word文档的方式
poi-tl(poi template language)是Word模板引擎,使用模板和数据创建很棒的Word文档。
方案 | 移植性 | 功能性 | 易用性 |
---|---|---|---|
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平台 | - | 复杂,完全不推荐使用 |
Thymeleaf | Java跨平台 | 使用Html方式的配置导出word,维护麻烦虽然可以在word模板用对应的语法写出后保存为html,但是不能所见即所得 | 不推荐 |
这里介绍的POI-TL也是一个免费的开源库,上手简单,维护方便,只需要学习一下他的语法便能正常配置和生成word
该库提供了maven和gradle的方式以1.12.2版本为例子
maven
<dependency>
<groupId>com.deepoove</groupId>
<artifactId>poi-tl</artifactId>
<version>1.12.2</version>
</dependency>
gradle
implementation 'com.deepoove:poi-tl:1.12.2'
引入模板后,其实已经具备导出渲染word模板的能力
/**
* 导出模板文件
*/
public void export(String templateId, String userId) throws Exception {
// 支持InputStream传参
var template = XWPFTemplate.compile("template.docx");
template.render(List.of("模拟数据", "模拟数据2"));
// 可以写入到Byte[]上传到文件存储
template.write(new FileOutputStream("output.docx"));
}
这里只用了三步便实现了word文件导出,
compile编译模板,也可以使用文件存储对象,传参使用inputStream
render 渲染数据,这里的数据结构需要与模板中的配置对应,后面会详细说明
write 输出到流,也可以输出到流转为Byte[],上传到文件存储对象
// 以流的方式进行输出
template.write(OutputStream stream);
// 比如文件流
template.write(new FileOutputStream("output.docx"));
// 比如网络流
response.setContentType("application/octet-stream");
response.setHeader("Content-disposition","attachment;filename=\""+"out_template.docx"+"\"");
// HttpServletResponse response
OutputStream out = response.getOutputStream();
BufferedOutputStream bos = new BufferedOutputStream(out);
template.write(bos);
bos.flush();
out.flush();
// 最后不要忘记关闭这些流。
PoitlIOUtils.closeQuietlyMulti(template, bos, out);
模板是Docx格式的Word文档,也可以使用Microsoft office、WPS Office、Pages等任何你喜欢的软件制作模板,也可以使用Apache POI代码来生成模板。
所有模板中的配置的标签都是以{ {开头,以}}结尾,标签可以出现在任何位置,包括页眉,页脚,表格内部,文本框等,表格布局可以设计出很多优秀专业的文档,推荐使用表格布局。
poi-tl模板遵循“所见即所得”的设计,模板和标签的样式会被完全保留。
数据类似于哈希或者字典,可以是Map结构(key是标签名称):
Map<String, Object> data = new HashMap<>();
data.put("name", "Sayi");
data.put("start_time", "2019-08-04");
可以是对象(属性名是标签名称):
public class Data {
private String name;
private String startTime;
private Author author;
}
数据可以是树结构,每级之间用点来分隔开,比如{ {author.name}}标签对应的数据是author对象的name属性值。
poi-tl是一种无逻辑「logic-less」的模板引擎,没有复杂的控制结构和变量赋值,只有标签。标签由前后两个大括号组成,{ {title}}是标签,{ {?title}}也是标签,title是这个标签的名称,问号标识了标签类型,接下来我们来看看有哪些默认标签类型(用户可以创建新的标签类型,这属于更高级的话题)。
{
{var}}
数据模型:
String
:文本TextRenderData
:有样式的文本HyperlinkTextRenderData
:超链接和锚点文本Object
:调用 toString() 方法转化为文本推荐使用工厂 Texts 构建文本模型。
put("name", "Sayi");
put("author", Texts.of("Sayi").color("000000").create());
put("link", Texts.of("website").link("http://deepoove.com").create());
put("anchor", Texts.of("anchortxt").anchor("appendix1").create());
所见即所得,标签的样式会应用到替换后的文本上,也可以通过代码设定文本的样式。
{
"text": "Sayi",
"style": {
"strike": false, //删除线
"bold": true, //粗体
"italic": false, //斜体
"color": "00FF00", //颜色
"underLine": false, //下划线
"fontFamily": "微软雅黑", //字体
"fontSize": 12, //字号
"highlightColor": "green", //背景高亮色
"vertAlign": "superscript", //上标或者下标
"characterSpacing" : 20 //间距
}
}
图片标签以@开始 {
{@var}} :图片只要是一个下载地址即可
数据模型:
String
:图片url或者本地路径,默认使用图片自身尺寸ByteArrayPictureRenderData
FilePictureRenderData
UrlPictureRenderData
推荐使用工厂 Pictures
构建图片模型
// 指定图片路径
put("image", "logo.png");
// svg图片
put("svg", "https://img.shields.io/badge/jdk-1.6%2B-orange.svg");
// 图片文件
put("image1", Pictures.ofLocal("logo.png").size(120, 120).create());
// 图片流
put("streamImg", Pictures.ofStream(new FileInputStream("logo.jpeg"), PictureType.JPEG)
.size(100, 120).create());
// 网络图片(注意网络耗时对系统可能的性能影响)
put("urlImg", Pictures.ofUrl("http://deepoove.com/images/icecream.png")
.size(100, 100).create());
// java图片,我们可以利用Java生成图表插入到word文档中
put("buffered", Pictures.ofBufferedImage(bufferImage, PictureType.PNG)
.size(100, 100).create());
FilePictureRenderData的结构体
{
"pictureType" : "PNG", // 图片类型
"path": "logo.png", // 图片路径
"pictureStyle": {
"width": 100, // 宽度
"height": 100 // 高度
},
"altMeta": "图片不存在" // 图片不存在显示的文字
}
表格标签以#开始:{
{#var}}
数据模型:
TableRenderData
推荐使用工厂 Tables
、 Rows
和 Cells
构建表格模型。
// 一个2行2列的表格
put("table0", Tables.of(new String[][] {
new String[] { "00", "01" },
new String[] { "10", "11" }
}).border(BorderStyle.DEFAULT).create());
// 第0行居中且背景为蓝色的表格
RowRenderData row0 = Rows.of("姓名", "学历").textColor("FFFFFF")
.bgColor("4472C4").center().create();
RowRenderData row1 = Rows.create("李四", "博士");
put("table1", Tables.create(row0, row1));
// 合并第1行所有单元格的表格
RowRenderData row0 = Rows.of("列0", "列1", "列2").center().bgColor("4472C4").create();
RowRenderData row1 = Rows.create("没有数据", null, null);
MergeCellRule rule = MergeCellRule.builder().map(Grid.of(1, 0), Grid.of(1, 2)).build();
put("table3", Tables.of(row0, row1).mergeRule(rule).create());
列表标签以*开始:{
{*var}}
数据模型:
List<String>
NumberingRenderData
推荐使用工厂 Numberings
构建列表模型。
代码示例
put("list", Numberings.create("Plug-in grammar",
"Supports word text, pictures, table...",
"Not just templates"));
区块对由前后两个标签组成,开始标签以?标识,结束标签以/标识:{
{?sections}}{
{/sections}}
使用示例
{
{?songs}}
{
{name}}
{
{/songs}}
可以用作判断
区块对开始和结束标签中间可以包含多个图片、表格、段落、列表、图表等,开始和结束标签可以跨多个段落,也可以在同一个段落,但是如果在表格中使用区块对,开始和结束标签必须在同一个单元格内,因为跨多个单元格的渲染行为是未知的。
区块对在处理一系列文档元素的时候非常有用,位于区块对中的文档元素可以被渲染零次,一次或N次,这取决于区块对的取值。
False或空集合
隐藏区块中的所有文档元素
非False且不是集合
显示区块中的文档元素,渲染一次
非空集合
根据集合的大小,循环渲染区块中的文档元素
循环内置变量
在循环中提供了一些内置变量,这些内置变量只能用于区块对中。
变量 | 类型 | 说明 |
---|---|---|
_index | int | 返回当前迭代从0开始的索引 |
_is_first | boolean | 辨别循环项是否是当前迭代的第一项。 |
_is_last | boolean | 辨别循环项是否是当前迭代的最后一项。 |
_has_next | boolean | 辨别循环项是否是有下一项。 |
_is_even_item | boolean | 辨别循环项是否是当前迭代间隔1的奇数项。 |
_is_odd_item | boolean | 辨别循环项是否是当前迭代间隔1的偶数项。 |
#this | object | 引用当前对象,由于#和已有表格标签标识冲突,所以在文本标签中需要使用=号标识来输出文本。 |
template.docx(注意:如果标签内要使用运算符,需要开启Spring表达式):
{
{?produces}}
{
{_index + 1}}. {
{=#this}}
{
{/produces}}
Spring Expression Language 是一个强大的表达式语言,支持在运行时查询和操作对象图,可作为独立组件使用,需要引入相应的依赖:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>5.3.18</version>
</dependency>
开启办法
为了在模板标签中使用SpringEL表达式,需要将标签配置为SpringEL模式:
/**
* 导出模板文件
*/
public void export(String templateId, String userId) throws Exception {
// 支持InputStream传参
var configBuilder = Configure.builder();
configBuilder.useSpringEL();
var config = configBuilder.build();
var template = XWPFTemplate.compile("template.docx", config);
template.render(List.of("模拟数据", "模拟数据2"));
// 可以写入到Byte[]上传到文件存储
template.write(new FileOutputStream("output.docx"));
}
{
{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 Documentation (deepoove.com)
FreeMarker、Velocity文本模板中可以通过三个标签设置图片路径、宽和高:, 但是Word模板不是由简单的文本表示,所以在渲染图片、表格等元素时提供了数据模型,它们都实现了接口 RenderData ,比如图片数据模型 PictureRenderData 包含图片路径、宽、高三个属性
文章浏览阅读4.8k次,点赞2次,收藏9次。基于spring框架 实现的奇门云网关 WMS端 通用API方法:package org.tempuri;import java.io.BufferedReader;import java.io.InputStream;import java.io.InputStreamReader;import java.io.StringReader;import java.io.Strin..._奇门wms struct
文章浏览阅读471次。原生应用开发框架React-Nativefacebook 推出的使用react,js开发的原生框架react官网Weex阿里巴巴开源的使用vue开发的原生框架weex官网Flutterdart语言,类似es6,如果对面向对象有深入理解的话,很快上手,google公司推出的Flutter官网React-Native基于react优雅的架构设计支持热重..._pc原生应用开发技术
文章浏览阅读9.7k次,点赞2次,收藏8次。在上一篇文章里面的官方demo建立索引方式使用的是最基本的索引,这里在介绍两种基础索引更多索引类别1--更快的检索IndexIVFFlat官方介绍:为了加快搜索速度,可以将数据集分割成几部分。我们在d维空间中定义Voronoi单元格,并且每个数据库矢量都落入其中一个单元格中。在搜索时,只有查询x所在单元中包含的数据库向量y与少数几个相邻查询向量进行比较。(划分搜索空间)这是通过Inde..._indexivfflat
文章浏览阅读1.7w次,点赞5次,收藏14次。IAR注释中文显示乱码,在Tools&gt;&gt;Options&gt;&gt;Editor&gt;&gt;Default character 选择正确的编码比如UTF-8,然后关闭文件,再次打开。_iar中文乱码
文章浏览阅读1.6k次。本文主要讲述过去一年内可观测平台 SLS 的改进和迭代。_数据中台 告警系统
文章浏览阅读854次。昨天在win7上装SQLServer数据库,写一下体会。首先,如果以前安装的话,要删除干净。我也找了半天的网络资料。1.把原来SQLServer的安装目录 C:\Program Files\Microsoft SQL Server 删除2.所有SQLServer相关的注册表全部删除,保证系统是没有装过SQLServer的状态注册表打开方式:开始-->运行-->rege_ghost封装sql 2000
文章浏览阅读363次,点赞7次,收藏8次。【代码】Linux中禁用ctrl alt del快捷键重启。_linx 关闭 ctrl alt del
文章浏览阅读4.7k次,点赞2次,收藏7次。需求:项目中有高度、宽度自适应需求,需要适应不同分辨率的高度及宽度,在不同分辨率下效果区别不会很大html代码如下:<template> <div id="home"> <div class="head" > <v-head></v-head> </div&..._vue3 dom元素获取距离浏览器工具栏的高度
文章浏览阅读6.8k次,点赞2次,收藏6次。以前在Spring框架中使用Spring Security需要我们进行大量的XML配置,但是,Spring Boot在这里依然有惊喜带给我们,我们今天就一起来看看。 毫无疑问,Spring Boot针对Spring Security也提供了自动配置的功能,这些默认的自动配置极大的简化了我们的开发工作,我们今天就来看看这个吧。创建Project并添加相关依赖 配置applicat..._spring-security配置允许访问ip
文章浏览阅读737次,点赞9次,收藏8次。检测到hyper-v 时,勾选自动安装。_vm17pro密钥许可证
文章浏览阅读1.5k次。修改/etc/my.cnfcharacter-set-server=utf8[client]default-character-set=utf8注释掉 sql_mode重新新建mysql表在hive的元数据库中执行以下SQL语句,然后重新创建刚才的表即可 。alter table COLUMNS_V2 modify column COMMENT varchar(256) character set utf8;alter table TABLE_PARAMS modify column PAR_sqoop --default-character-set=utf8
文章浏览阅读845次。一、简介 在用python写脚本时,通常需要处理xml格式的文件或字符串。由于json格式处理的方便性, 我们可将其转为json格式进行处理。 二、步骤 1、安装工具包xmltodict 在命令行输入:pip install xmltodict 2、在代码使用xmltodict.parse(xml_str)进行处理 如 def load_json(xml_path): #获取xml文件 xml_f..._python xml 转json 格式