需求:将数据生成到word模板文件
实现方式:使用ftl做模板文件,将数据写到word文档中
1、导包
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.28</version>
</dependency>
2、将做好的word模板文件另存为xml;再将xml文件的后缀改成ftl。然后就可以使用ftl的表达式写需要替换的内容(和写jsp类似)注意:word 模板最好用03版的,如果不是的话 先另存为03版的再另存为03版的模板(为了兼容,避免低版本的office无法打开高版本)
3、使用ftl表达式,取数据
//循环
<#list infos as info >
//嵌套的循环
<#list info.values as value > </#list>
</#list>
//if判断 ?? 表示非空 ?表示调用方法
<#if value.disPersonnel??>
//取值
${value.disPersonnel}
<#else>
</#if>
//调用日期格式化的方法
${workExp.startTime?string('yyyy-MM')}
//还有更多的ftl表达式 可根据需要单独百度ftl表达式
4、使用(代码很简单)
/**
* @Author xxx
* @Date 2020/4/19 - 15:52
**/
public class Data2Word {
public static void main(String[] args) throws Exception{
Configuration configuration = new Configuration(new Version("2.3.28"));
configuration.setDefaultEncoding("utf-8");
//模板文件存放目录
configuration.setDirectoryForTemplateLoading(new File("D:xxxx\\xxx"));
//模板文件名称
Template t = configuration.getTemplate("xxxx.ftl");
// 模拟数据
Map<String,Object> map = new HashMap<>();
List<Map> list = new ArrayList<>();
List<Integer> values = null;
for(int i = 0 ; i < 3 ; i ++){
values = new ArrayList<>();
for(int j = 0 ; j < 60; j ++){
values.add(j);
}
Map<String,Object> vMap = new HashMap<>();
vMap.put("values",values);
list.add(vMap);
}
//此处是字节输出流、也可以用文件输出流输出到指定目录、
ByteArrayOutputStream out = new ByteArrayOutputStream();
map.put("infos",list);
t.process(map, new OutputStreamWriter(out));
System.out.println("end");
}
/**
*
* @param data 数据源
* @param dir 模板文件存放的目录
* @param fileName 模板文件( 文件名 + 后缀)
* @param dst 输出目录
* @throws Exception
*/
public static void dataToWord(Object data,String dir, String fileName,String dst) throws Exception{
Configuration configuration = new Configuration(new Version("2.3.28"));
configuration.setDefaultEncoding("utf-8");
configuration.setDirectoryForTemplateLoading(new File(dir));
Template t = configuration.getTemplate(fileName);
t.process(data, new OutputStreamWriter(new FileOutputStream(dst)));
}
/**
*
* @param data 数据源
* @param dir 模板文件存放的目录
* @param fileName 文件名 + 后缀
* @return 字节输出流
* @throws Exception
*/
public static OutputStream dataToWord(Object data, String dir,String fileName) throws Exception{
Configuration configuration = new Configuration(new Version("2.3.28"));
configuration.setDefaultEncoding("utf-8");
configuration.setDirectoryForTemplateLoading(new File(dir));
Template t = configuration.getTemplate(fileName);
ByteArrayOutputStream out = new ByteArrayOutputStream();
OutputStreamWriter outputWriter = new OutputStreamWriter(out, "UTF-8");
BufferedWriter writer = new BufferedWriter(outputWriter);
t.process(data, writer);
return out;
}
/**
*
* @param data 数据源
* param dir 模板文件存放的目录
* @param fileName 文件名 + 后缀
* @param outputStream 一般用作浏览器输出(response.getOutputStream())
* @throws Exception
*/
public static void dataToWord(Object data,String dir, String fileName,OutputStream outputStream) throws Exception{
Configuration configuration = new Configuration(new Version("2.3.28"));
configuration.setDefaultEncoding("utf-8");
configuration.setDirectoryForTemplateLoading(new File(dir));
Template t = configuration.getTemplate(fileName);
t.process(data, new OutputStreamWriter(outputStream));
}
}
5、效果(文件太大了,拆成小文件,拆文件的方法未提供)
最后1、如果需求是每份表单结束后要分页,可在</#list> 循环标签结束前插入分页符,需要注意的是,有时分页不起效果,可以在做模板文件的时候,写几个特殊的字符,比如“aaaa”,找到对应的位置,在再插入分页符。反正都是些office的标签 多调试几次就能找到正确的位置。(因为不是专业搞office软件的,只能说些摸索的方式)
<wx:sect>
<w:p>
<w:pPr>
<w:rPr>
<w:rFonts w:fareast="宋体" w:hint="default"/>
<w:lang w:val="EN-US" w:fareast="ZH-CN"/>
</w:rPr>
</w:pPr>
<w:r>
<w:rPr>
<w:rFonts w:ascii="微软雅黑" w:eastAsia="微软雅黑" w:hAnsi="Calibri" w:cs="Times New Roman"/>
<w:b/>
<w:bCs/>
<w:kern w:val="44"/>
<w:sz w:val="28"/>
<w:szCs w:val="28"/>
</w:rPr>
<w:br w:type="page"/>
</w:r>
<w:r w:rsidRPr="00A77B55">
<w:rPr>
<w:rFonts w:ascii="微软雅黑" w:eastAsia="微软雅黑" w:hAnsi="微软雅黑" w:cs="Times New Roman"/>
<w:b/>
<w:noProof/>
<w:spacing w:val="40"/>
<w:sz w:val="28"/>
<w:szCs w:val="28"/>
</w:rPr>
<w:lastRenderedPageBreak/>
<w:t xml:space="preserve"></w:t>
</w:r>
</w:p>
<w:sectPr>
<w:ftr w:type="odd">
<w:p>
<w:pPr>
<w:pStyle w:val="a13"/>
<w:rPr>
<w:rFonts w:fareast="宋体" w:hint="default"/>
</w:rPr>
</w:pPr>
</w:p>
</w:ftr>
<w:pgSz w:w="11905" w:h="16837"/>
<w:pgMar w:top="1440" w:right="1440" w:bottom="1440" w:left="1440" w:header="720" w:footer="720" w:gutter="0"/>
<w:cols w:space="720"/>
</w:sectPr>
</wx:sect>
最后2、如果需求需要插入图片也简单,在模板中找到图片标签的位置,然后将图片转成base64的字符串 使用ftl标签替换就好了,和取普通数据一样,关键是找到图片标签的位置
总之使用ftl生成word文档会遇到一些奇奇怪怪的问题,代码很简单,主要是心细。假如遇到打开报“很抱歉,无法打开xxxx.doc,因为文件内容有问题”可能是因为字符串有特殊符号,组装值的时候把字符串转译一下,有好多类似的工具类,可自行百度。