- 之前操作EXCEL使用的是poi,用poi操作word有点复杂,且没有模板功能.放弃
- 找到了freemarker这个工具,他可以根据word的模板生成导出的word
话不多说,跟着我来一步一步走
首先 我默认你已经有个maven或者gradle工程了
先别急着找freemarker的依赖,查查自己已有的依赖中是否已经有freemarker了.
查这里
有这个包就说明已经有该依赖了,不需要在导入了
- 若没有该包,请导入该依赖项:
<!-- https://mvnrepository.com/artifact/org.freemarker/freemarker -->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.28</version>
</dependency>
准备工作结束了,开始创建word模板
新建一个word,做好自己的模板,附上我的模板
解释:
${}中的是后面要使用的变量.
我用第一个
${l.communityName}
来举例
l表示某个集合中的某个实体,communityName是他的属性,也就是这里要填入的数据
在后面的代码中会有这样的:
List<Community> communities = communityMapper.selectList(null);
Map<String,Object> dataMap = new HashMap<String, Object>();
dataMap.put("list", communities);
word设计好后,将其转为xml
对该word文件点击另存为,选择xml
保存好后,打开,若没有一个xml的格式,大家可以对其格式化,idea直接格式化就行了,notepad++需要下载 xml tools 插件
下载xml tools插件 请自行搜索
下来这步很重要 ,需要各位非常细心!!!
打开xml后,找到第一个变量的位置,可以通过’$'符号找
找到后,会发现,这样的代码
<w:tc>
<w:tcPr>
<w:tcW w:w="3040" w:type="dxa"/>
<w:vAlign w:val="center"/>
</w:tcPr>
<w:p w:rsidR="00C43AC5" w:rsidRPr="00C43AC5" w:rsidRDefault="00C43AC5">
<w:pPr>
<w:rPr>
<w:sz w:val="21"/>
</w:rPr>
</w:pPr>
<w:bookmarkStart w:id="0" w:name="_GoBack"/>
<w:r>
<w:rPr>
<w:rFonts w:hint="eastAsia"/>
<w:sz w:val="21"/>
</w:rPr>
<w:t>$</w:t>
</w:r>
<w:r>
<w:rPr>
<w:sz w:val="21"/>
</w:rPr>
<w:t>{</w:t>
</w:r>
<w:proofErr w:type="spellStart"/>
<w:r w:rsidR="00724485">
<w:rPr>
<w:rFonts w:hint="eastAsia"/>
<w:sz w:val="21"/>
</w:rPr>
<w:t>l</w:t>
</w:r>
<w:r w:rsidR="00724485">
<w:rPr>
<w:sz w:val="21"/>
</w:rPr>
<w:t>.</w:t>
</w:r>
<w:r w:rsidRPr="00C43AC5">
<w:rPr>
<w:sz w:val="21"/>
</w:rPr>
<w:t>communityName</w:t>
</w:r>
<w:proofErr w:type="spellEnd"/>
<w:r>
<w:rPr>
<w:sz w:val="21"/>
</w:rPr>
<w:t>}</w:t>
</w:r>
<w:bookmarkEnd w:id="0"/>
</w:p>
</w:tc>
仔细观察这段代码中每个<w:t>标签,你会发现,他将刚刚我们写好的变量给拆分了.
下来就对这段代码进行修改.
注意:!!!
整个代码不能有删改,否则word会出现问题~~
我们只需要将<w:t>标签内的内容合并起来,变成如下所示的代码:
<w:tc>
<w:tcPr>
<w:tcW w:w="3040" w:type="dxa"/>
<w:vAlign w:val="center"/>
</w:tcPr>
<w:p w:rsidR="00C43AC5" w:rsidRPr="00C43AC5" w:rsidRDefault="00C43AC5">
<w:pPr>
<w:rPr>
<w:sz w:val="21"/>
</w:rPr>
</w:pPr>
<w:bookmarkStart w:id="0" w:name="_GoBack"/>
<w:r>
<w:rPr>
<w:rFonts w:hint="eastAsia"/>
<w:sz w:val="21"/>
</w:rPr>
<w:t>${l.communityName}</w:t>
</w:r>
<w:r>
<w:rPr>
<w:sz w:val="21"/>
</w:rPr>
<w:t></w:t>
</w:r>
<w:proofErr w:type="spellStart"/>
<w:r w:rsidR="00724485">
<w:rPr>
<w:rFonts w:hint="eastAsia"/>
<w:sz w:val="21"/>
</w:rPr>
<w:t></w:t>
</w:r>
<w:r w:rsidR="00724485">
<w:rPr>
<w:sz w:val="21"/>
</w:rPr>
<w:t></w:t>
</w:r>
<w:r w:rsidRPr="00C43AC5">
<w:rPr>
<w:sz w:val="21"/>
</w:rPr>
<w:t></w:t>
</w:r>
<w:proofErr w:type="spellEnd"/>
<w:r>
<w:rPr>
<w:sz w:val="21"/>
</w:rPr>
<w:t></w:t>
</w:r>
<w:bookmarkEnd w:id="0"/>
</w:p>
</w:tc>
就是把<w:t>标签内的变量全部移动到第一个$后面,其他的<w:t>标签滞空.
接下来进行循环设置
找到需要循环的代码
将该段填入
<#list list as l>
第一个list表示循环,第二个表示
dataMap.put("list", communities);
dataMap的key
填入的位置
直观来看,是红色框内需要循环向下补充单元格
xml的代码中来看呢,
</w:tr>是单元格的一行结束的标签,找到他,下面就是需要循环的表格
注意!!!
<#list list as l>是需要结束标签的
结束标签:</#list>
另外,这个表格最后两个是时间格式的
如果就这样向后执行的话,会报这样的错
Tip: If you need a particular format only once, use ?string(pattern), like ?string('dd.MM.yyyy HH:mm:ss'), to specify which fields to display.
解决方法:
将这个代码
<w:t>${l.completionTime}</#if></w:t>
变为
<w:t>${l.completionTime?string('yyyy.MM.dd')}</#if></w:t>
处理数据为空的问题
当某个字段数据为空,且不处理时,会报错!
解决方法:
这里假设completionTime这个字段可能为空
<w:t><#if l.completionTime??>${l.completionTime?string('yyyy.MM.dd')}</#if></w:t>
加上
<#if l.completionTime??> 这个代码就行了
将word模板用java处理导出
将改好的xml后缀改为ftl
核心代码:
try {
List<Community> communities = communityMapper.selectList(null);
for (Community community : communities) {
Integer state = community.getState();
if(state==0){
community.setStateStr("未开始");
}else if (state==1){
community.setStateStr("进行中");
}else {
community.setStateStr("已完成");
}
}
Map<String,Object> dataMap = new HashMap<String, Object>();
dataMap.put("list", communities);
//Configuration 用于读取ftl文件
//new Configuration()构造方法内是当前依赖的版本号
Configuration configuration = new Configuration(new Version("2.3.28"));
configuration.setDefaultEncoding("utf-8");
/**
* 指定ftl文件所在目录的路径,而不是ftl文件的路径
* setClassForTemplateLoading(a,b)
* a代表某个类加载器,b是ftl文件所在的路径,实在a所在的路径之下
* 网上查了下有人说可以这么理解
* 假设a的路径是:org.ql 而ftl在目录org.ql.template下
* 那么这里需要写为:configuration.setClassForTemplateLoading(a.getClass, "/template下");
* 但是!!! 我试了不行
* 所以还是将那个包路径填为空 在后面将ftl文件的相对路径写入
*/
configuration.setClassForTemplateLoading(this.getClass(), "");
//输出文档路径及名称
File outFile = new File("E:/桌面/测试.doc");
//以utf-8的编码读取ftl文件
Template template = configuration.getTemplate("/template/老旧小.ftl", "utf-8");
Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile), "utf-8"), 10240);
template.process(dataMap, out);
out.close();
} catch (Exception e) {
e.printStackTrace();
}
实际上,我的ftl文件放在该类所在的同包下的template包下
结束!
效果: