使用Java + Freemarker 导出word文档
关于使用java导出word文档,网上有很多资料,但基本上来说使用freemarker模板导出的教程居多。所以这次基于网上查到的资料和自己的实践,记录下自己的实践过程,以便日后查阅,也希望能帮到一些人。
下面是基本的例子,以实现简单的word导出:
1. 组织word对应ftl模板
要导出的word模板的内容,启动拼音部分为要在代码种替换的部分。
模板
编辑好word后将文件另存为.xml文件,然后再将.xml文件后缀改为.ftl。打开ftl文件,依次将变量替换为用${}包裹。注意:替换的内容需要包裹在<w:t> </w:t>之中。
另外,最好使用全中文作为占位符。因为使用英文的话,转为xml时,word可能会将一个单词拆分成两个,比如我使用Title作为占位符,转化为xml后,搜索的时候一直找不到。然后你会发现,其实word将其拆分成T和itle。这种事也不是绝对的(同一个单词如果有不同的样式就会保存在不同的<w:r>中),所以只是建议,即便同一个单词被拆分了,也不用急等到后面就有解决方案。
word文档的结构
对于List类型的内容来说需要进行遍历。对于上面的数据结构来说,我们需要对list进行遍历。在这之前,我们首先了解一下word xml的大概结构
<w:wordDocument> <w:body> <w:p> <w:pPr> </w:pPr> <w:r> <w:rPr>属性:加粗,倾斜,字体颜色等</w:rPr> <w:t> 文本内容</w:t> </w:r> </w:p> </w:body> </<w:wordDocument>
- <w:p> 会包裹一段数据,(段落)
- <w:pPr> 段落的属性,可选元素。 段落属性的一些示例包括对齐方式、边框、断字覆盖、缩进、行距、底纹、文本方向和孤行控制
- <w:r> 它是具有一组共同属性(如格式设置)的文本区域。它可以包含多个<w:t>元素。如果示例文本中只有一个字是粗体,粗体将会分离到一个<w:r>中
- <w:rPr>用于指定<w:r>属性。 连续文本属性的一些示例包括粗体、边框、字符样式、颜色、字体、字号、斜体、字距调整、禁用拼写/语法检查、底纹、小号大写字母、删除线、文字方向和下划线
- <w:t> 实际的文本内容
下面我们用一个例子来说明,写了一些内容,并配置了颜色
示例
另存为xml文件后的部分代码
<w:p wsp:rsidR="0084377C" wsp:rsidRPr="002827FA" wsp:rsidRDefault="009C2113"> <w:pPr> <w:rPr> <w:color w:val="000000"/> </w:rPr> </w:pPr> <w:r> <w:rPr><w:rFonts w:hint="fareast"/></w:rPr> <w:t>哈哈</w:t> </w:r> <w:r wsp:rsidRPr="009C2113"> <w:rPr> <w:rFonts w:hint="fareast"/> <w:color w:val="FF0000"/> </w:rPr> <w:t>嗝</w:t> </w:r> <w:r wsp:rsidRPr="002827FA"> <w:rPr> <w:rFonts w:hint="fareast"/> <w:color w:val="000000"/> </w:rPr> <w:t>哈哈</w:t> </w:r> </w:p>
从上面可以清楚的看到,上面的内容在一个段落里包裹。同时在一个段落里可以设置多个不同的文字样式,这部分数据就会存放在 <w:r> 中,样式数据就存放在<w:rPr> 里面。
所以说如果我们需要遍历,首先要找到需要遍历的位置在哪里?找好以后就完成了一半的工作。例如上面的小案例,我们需要遍历学号和内容。 所以首先定位到 “xuehao” 所在的<w:p> 然后查找 “选项”所在的</w:p>。 然后将这么内容使用<#list> </#list>包裹就可以了。
<#list list as stu> <w:tr wsp:rsidR="00B362B3" wsp:rsidRPr="00B55103" wsp:rsidTr="00B55103"> <w:trPr><w:trHeight w:val="563"/></w:trPr> <w:tc> <w:tcPr><w:tcW w:w="4148" w:type="dxa"/><w:shd w:val="clear" w:color="auto" w:fill="auto"/></w:tcPr> <w:p wsp:rsidR="00B362B3" wsp:rsidRPr="00B55103" wsp:rsidRDefault="00B362B3"> <w:proofErr w:type="spellStart"/> <w:r wsp:rsidRPr="00B55103"><w:t>${stu.xuehao}</w:t></w:r> <w:proofErr w:type="spellEnd"/> </w:p> </w:tc> <w:tc> <w:tcPr><w:tcW w:w="4148" w:type="dxa"/><w:shd w:val="clear" w:color="auto" w:fill="auto"/></w:tcPr> <w:p wsp:rsidR="00B362B3" wsp:rsidRPr="00B55103" wsp:rsidRDefault="00B362B3"> <w:proofErr w:type="spellStart"/> <w:r wsp:rsidRPr="00B55103"><w:rPr><w:rFonts w:hint="fareast"/></w:rPr></w:r> <w:r wsp:rsidRPr="00B55103"><w:t>${stu.neirong}</w:t></w:r> <w:proofErr w:type="spellEnd"/> </w:p> </w:tc> </w:tr> </#list>
2. 添加freemarker依赖
<dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.30</version> </dependency>
3. 测试代码
package demo; import freemarker.template.Configuration; import freemarker.template.Template; import java.io.*; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class WordTest { private Configuration configuration = null; public WordTest() { configuration = new Configuration(); configuration.setDefaultEncoding("UTF-8"); } public static void main(String[] args) { WordTest test = new WordTest(); test.createWord(); } public void createWord() { Map<String, Object> dataMap = new HashMap<String, Object>(); getData(dataMap); configuration.setClassForTemplateLoading(this.getClass(), "/");//模板文件所在路径,此处我是存放在resource目录下 try { Template t = configuration.getTemplate("wordtemplate.ftl"); //获取模板文件 File outFile = new File("D:/outFile" + Math.random() * 10000 + ".doc"); //导出文件 Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile))); t.process(dataMap, out); //将填充数据填入模板文件并输出到目标文件 } catch (Exception e) { e.printStackTrace(); } } private void getData(Map<String, Object> dataMap) { dataMap.put("title", "标题"); dataMap.put("nian", "2020"); dataMap.put("yue", "09"); dataMap.put("ri", "08"); dataMap.put("shenheren", "李小龙"); List<Map<String, Object>> list = new ArrayList<Map<String, Object>>(); for (int i = 0; i < 10; i++) { Map<String, Object> map = new HashMap<String, Object>(); map.put("xuehao", i); map.put("neirong", "内容" + i); list.add(map); } dataMap.put("list", list); } }
4. 文件结构
文件结构
5. 导出文件效果
效果图