java如何用Freemarker导出word

前言:

在项目中应该会经常遇到导出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小伙伴在等你们啦,感兴趣的就赶紧来点击关注我把,哪里有不明白或有不同观点的地方欢迎留言。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

开心的码字达

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值