⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 juejin.cn/post/7135253150532894728 「llsydn」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注**微信公众号:【芋道源码】**有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

1.写在前面

很多时候,我们可能需要根据一个word模板,动态生成,我们所需要得一个word文档。比如,前面我们的电子签章系统《SpringBoot实现电子文件签字+合同系统!》。

很多人可能不知道怎么做?刚好这两天群里一位网友,假期放假中还在埋头苦干写 bug,不知如何动态生成 word 文档?

借这个机会,今天我们一起来手把手教大家如何动态生成 word 文档。

我们来看一下这个word模板,格式如下:

word模板

这样得一个word模板,我之前得做法是替换段落文本,那会有啥问题?

  1. 文本的格式可能会丢失。
  2. 替换内容标识,可能会读取不到(单词别隔开)。
  3. 无法动态循环输出一个list集合。
  4. table表格无法做到动态输出。

当然啦,因为我们之前的业务功能比较简单,使用之前的替换段落文本方式,也是能实现到,所以就一直没有去研究,有无更好的方式。

嘿,随着业务功能的不断深入,动态list集合,动态table,这些功能也要求要实现了。

那我们作为一个程序员,这不得深入研究嘛?

巧了,哥们这几天,也研究了一个方式,可以实现动态list集合,动态table的渲染,那就是**「freemarker模板引擎」**。

好了,废话不多说,直接进入主题了!!!

进入主题

2.freemarker实现

首先说一下实现步骤:通过将word模板,另存为.xml格式,然后将.xml文件后缀改成 .ftl,然后再使用freemarker模板引擎,将数据填充到.ftl,然后再输出成一个word文档。

实现步骤,还是挺清晰的。那接下来,我们来实现一下:

2.1 word模板

word模板

例如:我们有一个这样的api接口文档,然后我们的系统后台会配置管理了很多api接口,然后导出到一个word文件。

1.输出的word文件,要带上文档目录结构(左边)

2.api的内容,要有table表格

3.api接口是一个list集合

由此可见,如果还是用我以前的方式,估计实现起来就相当麻烦了。

那这里用freemarker,如何实现呢?freemarker的语法是:${xxxx},那我们来改造模板,格式如下

freemarker

这里为什么要用:${api.xxx}呢?我们来看一下freemarker是如何渲染一个list的?

<#list apiList as api>    
${api.apiName}
</#list>

看到这,估计大家都懂了把?

就是说我们有一个apiList集合对象,apiList as api,别名为api,进行遍历。${api.apiName} 就输出了apiName对应的属性的值。

看到这,我们的上面的模板,就很好理解了把?那我们来另存为xml。

另存为xml

2.2 .xml改.ftl

使用idea打开.ftl,然后格式化一下,搜索一下${api.apiName}

ftl格式化

这里要注意一下,可能会有些地方报错,例如下面:

报错

对于这些内容,我们改一下即可。

占位符渲染

参考${api.apiName}的格式即可。

好了,这里会有个问题,我们并没有看到有渲染一个list集合的?那这里,我们只能自己去构造了。

找到所有需要动态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目录下面即可,例如下面:

ftl模板

2.4 导出word

好了,我们测试一下,浏览器直接访问:http://localhost:8080/doExportApiDoc2Word?projectId=1

导出word

由此可见,导出的效果,还是蛮好的。

嘿,功能也实现了,我真是个聪明的小伙!!!^_^

聪明的小伙

2.5 office问题处理

好了,功能都正常了,接着就部署到正式环境了,测试了一下,导出的word也能打开。

然后通知业主方的人员测试下,嘿,问题来了:他们说,word文档打不开。

woc,什么情况?我这边都正常的喔?然后找他们要了截图,如下:

office打不开

看到这,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还是打不开,错误信息如下:

office打不开

上面说,xml字符非法,出现的位置:行6984,列43

这个问题,我们要怎么样快速定位到6984行呢?这里我们可以借助一个工具:Notepad++,会帮我们格式化。

可以发现,这里确实是有些特殊字符,回想一下我的功能,这里,好像是使用了换行符号。

office打不开

换行符号如下:

private static String LINESEPARATOR = "" + (char) 11 + "";

不要问我为啥用这个?这个是在网上找到的,当时试了一下,wps下面确实可以换行,觉得就挺神奇。

结果,最后还是栽在了这个换行符号上面!!!

行,那我们就换一种方式:改成下面这个:

private static String LINESEPARATOR = "<w:p></w:p>";

好了,测试一下,功能正常了。office能打开了!!!


今天,又是充实的一天!!!

哎呀,一股风吹过,头顶不禁有点凉意。

啥情况?我的头发呢?(卑微)

卑微


好了,以上就是 「freemarker实现word文档模板动态生成」 的分享了。

个人实操可能也不够全面,班门弄斧了。

如果觉得有收获的,帮忙点赞、评论、收藏一下呗!!!

点赞

文章目录
  1. 1. 1.写在前面
  2. 2. 2.freemarker实现
    1. 2.1. 2.1 word模板
    2. 2.2. 2.2 .xml改.ftl
    3. 2.3. 2.3 freemarker渲染
    4. 2.4. 2.4 导出word
    5. 2.5. 2.5 office问题处理