乐趣区

关于java:我是如何使用freemarker生成Word文件的

举荐:亲自体验,数次踩坑,遂撰写此文,以备各位不时之需。

背景

一天,产品经理递给我了一份 word 报告,我定睛一看

这个文档有大大小小的题目层级,还有排版好的段落、各种高深莫测的饼图、走势图,当然还少不了色彩循环交替的报表。粗劣水平不亚于小明同学的学习报告。

筹备

鲁迅:身为一名 Java 程序员,任何时候都不要遗记站在伟人的肩膀上。


通过某歌搜寻关键词:java+word+ 导出,我立马得出了很多成熟的计划,通过横向、纵向比拟,再联合本次报告款式比拟多、用户可灵便抉择不同模块导出的特点,最终,我决定应用Freemarker 动静替换模版数据来导出 word 文档。至于导出文档的最终格局,有两种抉择:

那到底应用 doc 还是 docx 格局的文档?
每当人生当中每次面临抉择我都很谨慎。最终我抉择应用 docx 格局(起因文末会讲),然而为了让大家有更多的抉择,满足更多的业务场景,借此机会,小明会给大家别离介绍应用 freemarker 导出两种格局的 word 文档形式。

思路

FreeMarker 是一个基于 Java 的模板引擎,最后专一于应用 MVC 软件架构生成动静网页。然而,它是一个通用的模板引擎,不依赖于 servlets 或 HTTP 或 HTML,因而它通常还用于生成源代码,配置文件或电子邮件。

此时,咱们用它动静生成 xml 文件,进而导出 word 文档。

整体流程如下:

筹备

  • WPS

由金山软件股份有限公司公布,用于办公软件最罕用的文字编辑、表格、演示稿等性能。

对,就是这个国产的办公软件。我也是第一次发现在导出文档这件事上,它如多年好友般敌对。(word 解析后的 xml 文件浏览性很强,个别人我不通知他)

  • 开发工具(IDEA、Visual Studio Code 等)

你喜爱的,棘手的,就是最好的。

实现

集成 Freemarker 模版引擎

本次我的项目应用的框架仍旧是 Springboot,这个框架在集成各个组件体现都很便捷,不再赘述,这次集成 Freemarker 也不例外。

  • 首先咱们在我的项目中削减依赖spring-boot-starter-freemarker

pom.xml 文件如下所示:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
  • 依照默认约定,咱们能够在 resources 下创立一个 templates 文件夹(查看 FreeMarkerProperties 源码能够发现默认目录就是这个),用于寄存模版文档。
  • application.yml 减少配置
spring:
  freemarker:
    template-loader-path: classpath:/templates
    cache: false # 开发环境缓存敞开
    suffix: xml
    charset: UTF-8

生成 doc 格局的文档

这里先拿应用 freemarker 导出 doc 格局的 word 文档举例。

  • 首先将 docxTemplate.docx(调整好款式的模版文档)另存为 WORD 2003 XML 文档(*.xml)


此处命名为 docTemplete.xml,应用编辑工具首次关上时,会发现这个文档外面是压缩的 xml,因而咱们首先须要格式化一下。

留神:如果你应用的是 Visual Studio Code 开发工具,肯定要查看你所应用的 xml 格式化插件,是否会优化你的 xml 标签。比方:<w:rPr>会变成 <rPr>。应用Visual Studio Code 的同学,oh my god ! 小明在这里举荐大家应用这个插件:XML Language Support by Red Hat

  • 当初,咱们就应用 freemarker 语法编辑 docTemplete.xml,比方应用占位符 ${} 替换以后文档中的文本,以达到动静生成文本的目标,间接上代码。
public static Configuration getConfiguration(){
        // 创立配置实例
        Configuration configuration = new Configuration(Configuration.VERSION_2_3_28);
        // 设置编码
        configuration.setDefaultEncoding("utf-8");
        configuration.setClassForTemplateLoading(WordUtil.class, "/templates");
        return configuration;
}

    /**
     * 生成 doc 文件
     *
     * @param ftlFileName 模板 ftl 文件的名称
     * @param params      动静传入的数据参数
     * @param outFilePath 生成的最终 doc 文件的保留残缺门路
     */
    public void ftlToDoc(String ftlFileName, Map params, String outFilePath) {
        try {
            /** 加载模板文件 **/
            Template template = configuration.getTemplate(ftlFileName);
            /** 指定输入 word 文件的门路 **/
            File docFile = new File(outFilePath);
            FileOutputStream fos = new FileOutputStream(docFile);
            Writer bufferedWriter = new BufferedWriter(new OutputStreamWriter(fos, "utf-8"), 10240);
            template.process(params, bufferedWriter);
            if (bufferedWriter != null) {bufferedWriter.close();
            }
        } catch (TemplateException e) {e.printStackTrace();
        } catch (IOException e) {e.printStackTrace();
        }
    }

生成 docx 格局的文档

高能预警! 在胜利应用 Freemarker 动静导出 doc 格局的文档之后,置信大家和我的情绪一样十分冲动。但以上操作只是一个小铺垫,接下来咱们来看看如何实现 docx 格局的文档导出,小明置信肯定会让各位看官大跌眼镜!不,大开眼界!
首先,通知大家一个机密:docx 格局的文档其实是一个 ZIP 格局的压缩文件哦! 什么?你不信?验证如下:

  • windows 的小伙伴

将 docx 文档批改为 ZIP 格局(批改.docx 后缀名为.zip),而后通过解压工具解压。

  • MacOS 的小伙伴

间接应用 unzip 命令解压 word 文档,解压过后咱们会发现该文档其实还有本人的 目录构造


当然,这么多文件咱们不用一一知悉,只需关注小明红线标注的文件和目录即可:

  • document.xml 文件用于寄存外围数据,文字,表格,图片援用等
  • media 目录用于寄存所有文档的图片
  • _rels 目录下的 document.xml.rels 里寄存的是配置信息,比方图片援用关系,即在 document.xml 中援用 id 对应 media 中的哪个图片。
  • 获取 zip 里的 document.xml 文档以及_rels 文件夹下的 document.xml.rels 文档
  • 不言而喻,如果咱们要想依据数据动静导出不同的 word 文档,只须要:通过 freemarker 将本次数据填充到 document.xml 中,并将图片配置信息填充至 document.xml.rels 文档里,再用文件流把本次图片写入到 media 目录下替换曾经存在的图片,最初把填充过内容的 document.xml、document.xml.rels 以及 media 用流的形式写入 zip 即可输入 docx 文档!上代码。

好吧,限于篇幅,代码见文末 Github 地址

问题及解决方案

当然,大家在第一次尝试去干某一件事时,都不肯定是欲速不达的。就比方在导出 word 时,就可能会遇到以下问题。​

特殊字符

问题:有些文本数据中不免含有特殊字符,如:< > @ ! $ & 等等。

解决方案 :这些特殊字符如果不进行本义,就会引起 word 打不开的景象,比方表格中的超链接的& 符号,就须要替换为&amp;,如果你的文档用 office 关上时提醒文件损坏,九成是因为特殊符号引起的,咱们能够关上 documet.xml 定位报错地位;当然还有终极计划,咱们能够利用 Freemarker 的语法间接在模板中应用<![CDATA[]]> 解决。比方:

 <w:t><![CDATA[ ${article.title} ]]></w:t>

图片变形

问题:因为 echarts 生成的图表是响应式的,不同的屏幕大小、分辨率,会造成每次前端传过来的图片宽高比例不统一,如果还间接将图片依照之前的比例放进文档,会造成生成后文档中的图片变形。

思路:首先将文档中的图片设置为原图,而后锁定宽高比,将图片调整到适合大小,解压文档从document.xml,失去此时 word 中该图片宽高对应的值,如下所示:

要想保障不同像素比例的宽高在文档中不变形,咱们须要固定 cy 的值,而后依据固定比例动静求得以后像素比例图片在 word 中代表的宽 cx 的值。计算方法如下所示:
公式:

a/b = x/y

其中,a 示意图片在 word 中宽的数值,b 代表图片在 word 中高的数值,x 示意前端传过来图片的宽(单位:像素),y 示意前端传过来图片的高(单位:像素)。因而,已知 b、x、y, 依据公式,咱们即可求出 a;

我就是文末

当然,还有用一些其余注意事项:

  • 如果 word 中的模块比拟多的话,应用 Freemarker 语法要认真一点;
  • 为什么小明最终抉择导出 docx 格局的文档呢?(还不是因为产品经理的需要嘛)因为 doc 格局的文档,小明尝试导出后,发现该文档并不是一个非法的 doc 文档,体现在:不能在手机上(微信、钉钉)失常预览,office 提醒以 xml 模式关上等。因而在导出 doc 文档时,通过 Freemaker 填充 document.xml 后失去的并不是一个非法的 word 文档,查了相干材料,还须要借助第三方工具进行签名,而签名还须要在 windows 零碎下能力实现,然而咱们平时用的生产环境都是 Linux……因而,思考再三,再三衡量,最终抉择导出 docx 格局的文档。这种形式再适宜不过,而且还能保障在以后支流 APP 上都能失常预览。
  • 敲黑板!导出 docx 文档最重要的一个思维是将本次数据写入笼罩模版文件(在商业中,相当于借壳上市),从新输入一个 zip 格局压缩的文件,这个文件就是咱们最终想要的文档。

以上,就是小明 word 导出的前前后后,如果你也已经遇到过或者当初正好遇到 word 文档导出开发的问题,欢送一起探讨交换。

相干链接

我上传了工具类,蕴含 doc、docx 的导出,以及导出 word 文档时特殊符号本义,还有图片 Base64 转换成文件输入的办法。
GitHub 地址:https://github.com/WhenCoding…

本文可转载,但需申明原文出处。程序员小明,一个很少加班的程序员。欢送关注微信公众号,获取更多优质文章。

退出移动版