举荐:亲自体验,数次踩坑,遂撰写此文,以备各位不时之需。
背景
一天,产品经理递给我了一份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打不开的景象,比方表格中的超链接的&
符号,就须要替换为&
,如果你的文档用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...
本文可转载,但需申明原文出处。 程序员小明,一个很少加班的程序员。欢送关注微信公众号,获取更多优质文章。