前言:
在项目中应该会经常遇到导出word需求,Java中有5种方式导出word:
所用技术 | 优点 | 缺点 |
Jacob | 功能强大 | 代码量大,设置样式繁琐;需要windows平台支持,无法跨平台 |
Apache POI | 读写excel功能强大、操作简单 | 一般只用它读取word,能够创建简单的word,不能设置样式,功能太少 |
Java2word | 功能强大,操作简单 | 能满足一般要求,不支持07格式,国人开发的,参考资料较多,需要windows平台支持 |
iText | 功能全,能满足一般要求 | 不能直接生成或操作doc文档,只能生成rtf格式的文档,rtf也可以用word打开 |
JSP | 操作简单,代码量少 | 能把当前页面导出简单的word,不能设置样式,美观性差,无法操作word |
Freemarker | 代码量少,样式、内容容易控制,打印不变形,完全符合office标准 | 需要提前设计好word模板,把需要替换的地方用特殊标记标出来 |
具体选择哪种方式实现Java导出word,要根据自己的需求和实际情况灵活选择,今天这篇文章主要讲解下java怎么结合freemarker导出word的,我选择freemarker主要因为相对于poi导出word,它更灵活。
正文:
Freemarker导出word的思路是,先把word文件中插入特殊的字符串占位符,另存为xml,然后将xml翻译为FreeMarker模板,最后用java来解析FreeMarker模板,编码调用FreeMarker实现文本替换并输出Doc。
一、把插入占位符word文件另存为xml
网上很多说不建议用wps,但是我用的wps发现也没有太大的影响,这是我word制作的表格。
然后另存为Word XML文档,把生成的xml放到WEB-INF目录下,我在这个目录下又建了word目录,最后我放到word目录下啦
直接打开xml会发现,格式很错乱,代码挤到一块了,如下图
我们可以通过idea格式化代码的快捷键,把代码格式化下,方便阅读
Ctrl+Alt+L
二、引入Freemarker的maven依赖和lombok的依赖
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.20</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.18</version>
</dependency>
三、编写模板展示的类和导出的工具类以及测试类代码
User类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private String name;
private String sex;
private String age;
private String phone;
private String email;
}
导出的工具类:
public class exportWord {
public void export(Map<String, Object> map) {
try {
Configuration configuration = new Configuration();
configuration.setDefaultEncoding("UTF-8");
//模板文件配置路径
configuration.setDirectoryForTemplateLoading(new File("F:\\dao\\exportWord\\src\\main\\webapp\\WEB-INF\\word"));
configuration.setTemplateExceptionHandler(TemplateExceptionHandler.IGNORE_HANDLER);
//文件输出路径,文件名
File outFile = new File("E:\\测试模板.doc");
//扫描模板路径下 模板文件
Template template = configuration.getTemplate("测试模板.xml", "UTF-8");
Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile), "UTF-8"), 10240);
template.process(map, out);
out.flush();
out.close();
System.out.println("导出完成");
} catch (IOException e) {
e.printStackTrace();
} catch (TemplateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
测试类:
public class Test {
public static void main(String[] args) {
exportWord ew = new exportWord();
Map<String,Object> dataMap = new HashMap<String,Object>();
List<User> list = new ArrayList<User>();
User user1 = new User("ada", "男", "18", "10000000", "asc@126.com");
User user2 = new User("ada", "男", "18", "10000000", "asc@126.com");
User user3 = new User("ada", "男", "18", "10000000", "asc@126.com");
list.add(user1);
list.add(user2);
list.add(user3);
dataMap.put("userList", list);
ew.export(dataMap);
}
}
四、如果数据是集合,需要遍历展示的
找到 xml文件中 <w:tr> 标签 用 <#list map中键值 as 别名><w:tr></w:tr></#list> 标签包裹在内即可
五、启动测试类,看导出效果
六、 常见bug
顺利的话,你应该会出现如下图的bug,你只要格式化代码啦,按照这个错误报的行数,去看下,就知道为什么了,原因是word转xml的时候解析错误啦,需要去手动去调节下
freemarker.core.ParseException: Parsing error in template "测试模板.xml" in line 320, column 48:
Encountered "<", but was expecting one of:
<STRING_LITERAL>
<RAW_STRING>
"false"
"true"
<INTEGER>
<DECIMAL>
"."
"+"
"-"
"!"
"["
"("
"{"
<ID>
at freemarker.core.FMParser.generateParseException(FMParser.java:4672)
at freemarker.core.FMParser.jj_consume_token(FMParser.java:4543)
at freemarker.core.FMParser.UnaryExpression(FMParser.java:340)
at freemarker.core.FMParser.MultiplicativeExpression(FMParser.java:452)
at freemarker.core.FMParser.AdditiveExpression(FMParser.java:402)
at freemarker.core.FMParser.RangeExpression(FMParser.java:573)
at freemarker.core.FMParser.RelationalExpression(FMParser.java:528)
at freemarker.core.FMParser.EqualityExpression(FMParser.java:493)
at freemarker.core.FMParser.AndExpression(FMParser.java:602)
at freemarker.core.FMParser.OrExpression(FMParser.java:625)
at freemarker.core.FMParser.Expression(FMParser.java:238)
at freemarker.core.FMParser.StringOutput(FMParser.java:1076)
at freemarker.core.FMParser.Content(FMParser.java:2550)
at freemarker.core.FMParser.OptionalBlock(FMParser.java:2761)
at freemarker.core.FMParser.Root(FMParser.java:2933)
at freemarker.template.Template.<init>(Template.java:193)
at freemarker.cache.TemplateCache.loadTemplate(TemplateCache.java:419)
at freemarker.cache.TemplateCache.getTemplate(TemplateCache.java:330)
at freemarker.cache.TemplateCache.getTemplate(TemplateCache.java:205)
at freemarker.template.Configuration.getTemplate(Configuration.java:740)
at freemarker.template.Configuration.getTemplate(Configuration.java:681)
at com.util.exportWord.export(exportWord.java:25)
at com.util.test.Test.main(Test.java:54)
我们提前写好的占位符有时候转完后会错误,被拆解了,只要把多余的部分删除就可以啦,我们看下<w:r></w:r>标签是一个标签,把多余的删掉即可,删完后如下。
<w:tc>
<w:tcPr>
<w:tcW w:w="2027" w:type="dxa"/>
</w:tcPr>
<w:p>
<w:pPr>
<w:jc w:val="center"/>
<w:rPr>
<w:rFonts w:asciiTheme="minorEastAsia" w:hAnsiTheme="minorEastAsia"/>
<w:kern w:val="0"/>
<w:sz w:val="24"/>
<w:szCs w:val="20"/>
</w:rPr>
</w:pPr>
<w:r>
<w:rPr>
<w:rFonts w:asciiTheme="minorEastAsia" w:hAnsiTheme="minorEastAsia"/>
<w:kern w:val="0"/>
<w:sz w:val="24"/>
<w:szCs w:val="20"/>
</w:rPr>
<w:t>${user.name}</w:t>
</w:r>
</w:p>
</w:tc>
总结:
freemarker导出word还是很方便的,转化后的xml咋看很复杂,其实认真看下,规律还是很明显的,改动几个保存下,你就试出作用啦。
我是阿达,一名喜欢分享知识的程序员,时不时的也会荒腔走板的聊一聊电影、电视剧、音乐、漫画,这里已经有373小伙伴在等你们啦,感兴趣的就赶紧来点击关注我把,哪里有不明白或有不同观点的地方欢迎留言。