继上篇导出一个完美的doc后,业务又发生了变化必须生成docx格式的word才行(哭泣)。
但是利用freemarker生成的word文档(doc/docx)利用notepad++打开是xml格式。而正常的文档格式利用notepad打开是乱码。很明显,就算你废了九牛二虎之力导出来的word OFFICE也绝对打开不了(WPS作为小可爱确可以打开= =)。
例如下图:
目前你生成的doc/docx底层还是xml格式的,转pdf或者在手机上看都只能看到xml文件。
MS-Office下的word在2007以后后缀基本是以.docx结尾,是用一种xml格式的在存储数据(.doc是用二进制存储数据),这就为使用freemarker提供的条件,如果把template.docx,重命名成template.zip,再用word一样是可以打开的,如果有WinRAR之类的压缩工具打开会发现如下目录结构
我们用office工具打开看到的内容其事就存放在在这个document.xml里面!,打开看看(document.xml默认是不换行的,我用Nodpad++打开,然后下载nodpad插件Xml-tool格式化后,具体安装可参考Nodepad
格式化xml)在这个xml就是以这种格式存储的数据,只需要将我们需要的内容变成一个变量,然后通过freemarker来解析这xml,让后用解析后的xml,把template.zip里面的document.xml替换掉,然后将这个template.zip解压成data.docx,那么这个data.docx,就包含了我们需要的数据
来源:https://blog.csdn.net/u013076044/article/details/79236000
上面这段话我翻看了近100篇博客才找到。。真的是受益匪浅
导入步骤如下:
- 处理模版对应的docx,和我上篇一样替换变量就好啦。(插入图片的话,见这篇博客https://blog.csdn.net/SOME___ONE/article/details/52562743)
- 将你的docx文件重命名x.zip,用压缩包工具打开它。将word/document.xml copy出来!
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文档!
- 写代码~
@Test
public void ttt() throws Exception {
/** 初始化配置文件 **/
Configuration configuration = new Configuration();
/** 设置编码 **/
/** 我的ftl文件是放在D盘的**/
String fileDirectory = "G:\\work_code";
/** 加载文件 **/
configuration.setDirectoryForTemplateLoading(new File(fileDirectory));
/** 加载模板 **/
Template template = configuration.getTemplate("document.xml");
/** 准备数据,这里是我自己的业务。就是把变量赋值上${xxxx}去啦 **/
Map<String,String> dataMap = detailService.showWordDetail();
/** 指定输出word文件的路径 **/
String outFilePath = "G:\\work_code\\data.xml";
File docFile = new File(outFilePath);
FileOutputStream fos = new FileOutputStream(docFile);
OutputStreamWriter oWriter = new OutputStreamWriter(fos);
Writer out = new BufferedWriter(new OutputStreamWriter(fos),10240);
template.process(dataMap,out);
if(out != null){
out.close();
}
ZipInputStream zipInputStream = ZipUtils.wrapZipInputStream(new FileInputStream(new File("G:\\work_code\\1.zip")));
ZipOutputStream zipOutputStream = ZipUtils.wrapZipOutputStream(new FileOutputStream(new File("G:\\work_code\\test.docx")));
String itemname = "word/document.xml";
ZipUtils.replaceItem(zipInputStream, zipOutputStream, itemname, new FileInputStream(new File("G:\\work_code\\data.xml")));
System.out.println("success")