freemarker实现word文档模板动态生成
1.写在前面
很多时候,我们可能需要根据一个word模板,动态生成,我们所需要得一个word文档。
这个案例得word模板,有什么弊端呢?我们来看一下这个word模板,格式如下:
这样得一个word模板,我之前得做法是替换段落文本
,那会有啥问题?
- 文本的格式可能会丢失。
- 替换内容标识,可能会读取不到(单词别隔开)。
- 无法动态循环输出一个list集合。
- table表格无法做到动态输出。
当然啦,因为我们之前的业务功能比较简单,使用之前的替换段落文本
方式,也是能实现到,所以就一直没有去研究,有无更好的方式。
嘿,随着业务功能的不断深入,动态list集合,动态table,这些功能也要求要实现了。
那我们作为一个程序员,这不得深入研究嘛?
巧了,哥们这几天,也研究了一个方式,可以实现动态list集合,动态table的渲染,那就是freemarker模板引擎。
好了,废话不多说,直接进入主题了!!!
2.freemarker实现
首先说一下实现步骤:通过将word模板,另存为.xml
格式,然后将.xml
文件后缀改成 .ftl
,然后再使用freemarker模板引擎,将数据填充到.ftl
,然后再输出成一个word
文档。
实现步骤,还是挺清晰的。那接下来,我们来实现一下:
2.1 word模板
例如:我们有一个这样的api接口文档,然后我们的系统后台会配置管理了很多api接口,然后导出到一个word文件。
1.输出的word文件,要带上文档目录结构(左边)
2.api的内容,要有table表格
3.api接口是一个list集合
由此可见,如果还是用我以前的方式,估计实现起来就相当麻烦了。
那这里用freemarker,如何实现呢?freemarker的语法是:${xxxx},那我们来改造模板,格式如下
这里为什么要用:${api.xxx}呢?我们来看一下freemarker是如何渲染一个list的?
<#list apiList as api>
${api.apiName}
</#list>
看到这,估计大家都懂了把?
就是说我们有一个apiList
集合对象,apiList as api
,别名为api
,进行遍历。${api.apiName}
就输出了apiName对应的属性的值。
看到这,我们的上面的模板,就很好理解了把?那我们来另存为xml。
2.2 .xml改.ftl
使用idea打开.ftl,然后格式化一下,搜索一下${api.apiName}
这里要注意一下,可能会有些地方报错,例如下面:
对于这些内容,我们改一下即可。
参考${api.apiName}
的格式即可。
好了,这里会有个问题,我们并没有看到有渲染一个list集合的?那这里,我们只能自己去构造了。
找到所有需要动态list渲染的内容,然后再加上 <#list >
标签
看到这里,估计大家都知道怎么改.ftl
模板了吧?这里就不在一一列举了。
2.3 freemarker渲染
- 如果是使用springboot,那我们的pom.xml文件,可以添加下面的依赖:
<!-- freemarker-starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
<version>2.3.12.RELEASE</version>
</dependency>
当然,直接引入fremarker依赖也是可以的,例如下面:
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.31</version>
</dependency>
- 代码实现
@Autowired
private Configuration configuration;
@GetMapping("/doExportApiDoc2Word")
public void doExportApiDoc2Word(HttpServletResponse response,
@ApiParam @RequestParam(value = "projectId") String projectId){
try {
//返回word文档
String fileName = URLEncoder.encode("接口文档" + DateUtil.format(new Date(), "yyyyMMddHHmmss"), "UTF-8");
response.setCharacterEncoding("UTF-8");
response.addHeader("Content-Disposition", "attachment; filename="" + fileName + ".docx"");
//获取apiDoc所需要的数据
Map<String, Object> dataModel = llsydnService.getApiDocData(projectId);
//加载模板
Template template = configuration.getTemplate("apiDoc.ftl", "utf-8");
//渲染模板
template.process(dataModel, response.getWriter());
//response的Writer不需要我们手动关,tomcat会帮我们关的
} catch (Exception e) {
log.error("导出word异常:", e);
}
}
由上可见,基本上都不需要我们操作,我们只需要构造出对象的数据,传给freemarker引擎即可。
我们要做的,就是定义好.ftl
模板,构造好所需的参数。
这里说明一下,这个apiDoc.ftl
模板,我们默认是放在templates目录下面即可,例如下面:
2.4 导出word
好了,我们测试一下,浏览器直接访问:http://localhost:8080/doExportApiDoc2Word?projectId=1
由此可见,导出的效果,还是蛮好的。
嘿,功能也实现了,我真是个聪明的小伙!!!^_^
2.5 office问题处理
好了,功能都正常了,接着就部署到正式环境了,测试了一下,导出的word也能打开。
然后通知业主方的人员测试下,嘿,问题来了:他们说,word文档打不开。
woc,什么情况?我这边都正常的喔?然后找他们要了截图,如下:
看到这,Microsoft Word
?这个是office?哎呀,我本地用的是wps
,功能都挺正常的。
然后我就叫业主方人员,用wps
试试,嘿,果然是这个问题,wps可以打开,office打不开。
这就奇怪了,难道这是个正常的问题?然后百度了一下,确实,很多人都遇到了这个问题。
基本上都是过了,结果发现,解决的问题也是很简单,就是导出的格式,要从dcox
改成doc
@Autowired
private Configuration configuration;
@GetMapping("/doExportApiDoc2Word")
public void doExportApiDoc2Word(HttpServletResponse response,
@ApiParam @RequestParam(value = "projectId") String projectId){
try {
//返回word文档
String fileName = URLEncoder.encode("接口文档" + DateUtil.format(new Date(), "yyyyMMddHHmmss"), "UTF-8");
response.setCharacterEncoding("UTF-8");
response.addHeader("Content-Disposition", "attachment; filename="" + fileName + ".doc"");
//获取apiDoc所需要的数据
Map<String, Object> dataModel = llsydnService.getApiDocData(projectId);
//加载模板
Template template = configuration.getTemplate("apiDoc.ftl", "utf-8");
//渲染模板
template.process(dataModel, response.getWriter());
//response的Writer不需要我们手动关,tomcat会帮我们关的
} catch (Exception e) {
log.error("导出word异常:", e);
}
}
注意,上面的
docx
改成doc
,即可。
我这边还遇到一个问题,就是当我改成doc得时候,office还是打不开,错误信息如下:
上面说,xml字符非法,出现的位置:行6984,列43
这个问题,我们要怎么样快速定位到6984行呢?这里我们可以借助一个工具:Notepad++
,会帮我们格式化。
可以发现,这里确实是有些特殊字符,回想一下我的功能,这里,好像是使用了换行符号。
换行符号如下:
private static String LINESEPARATOR = "" + (char) 11 + "";
不要问我为啥用这个?这个是在网上找到的,当时试了一下,wps下面确实可以换行,觉得就挺神奇。
结果,最后还是栽在了这个换行符号上面!!!
行,那我们就换一种方式:改成下面这个:
private static String LINESEPARATOR = "<w:p></w:p>";
好了,测试一下,功能正常了。office能打开了!!!
今天,又是充实的一天!!!
哎呀,一股风吹过,头顶不禁有点凉意。
啥情况? 我的头发呢?(卑微)
好了,以上就是 freemarker实现word文档模板动态生成 的分享了。
个人实操可能也不够全面,班门弄斧了。
如果觉得有收获的,帮忙点赞、评论、收藏
一下呗!!!
转载自:https://juejin.cn/post/7135253150532894728