记录一次用Java生成word文档的经验

业务背景

最近接到一个需求需要将学员的基本信息生成word格式的内容,word的格式如下图所示

在这里插入图片描述

开发的任务就是将学员的信息替换掉表格中的** 即可,感觉还蛮简单的。

相信大家以前做的最多的是Java和Excel的转换的功能比较多吧,我也是如此,之前也没有做过word转换的功能,所以此次接到这个任务我也是一样,从网上找方法,在网上找了一些博客论坛大概有几种方法可以尝试。

几种解决思路

1:Jacob是Java-COM Bridge的缩写,它在Java与微软的COM组件之间构建一座桥梁。使用Jacob自带的DLL动态链接库,并通过JNI的方式实现了在Java平台上对COM程序的调用。DLL动态链接库的生成需要windows平台的支持。该方案只能在windows平台实现,是其局限性。

2:Apache POI包括一系列的API,它们可以操作基于MicroSoft OLE 2 Compound Document Format的各种格式文件,可以通过这些API在Java中读写Excel、Word等文件。他的excel处理很强大,对于word还局限于读取,目前只能实现一些简单文件的操作,不能设置样式。

3:Java2word是一个在java程序中调用 MS Office Word 文档的组件(类库)。该组件提供了一组简单的接口,以便java程序调用他的服务操作Word 文档。 这些服务包括: 打开文档、新建文档、查找文字、替换文字,插入文字、插入图片、插入表格,在书签处插入文字、插入图片、插入表格等。填充数据到表格中读取表格数据 ,1.1版增强的功能: 指定文本样式,指定表格样式。如此,则可动态排版word文档。是一种不错的解决方案。

4:iText是著名的开放源码的站点sourceforge一个项目,是用于生成PDF文档的一个java类库。通过iText不仅可以生成PDF或rtf的文档,而且可以将XML、Html文件转化为PDF文件。功能强大。

5:JSP输出样式,该方案实现简单,但是处理样式有点缺陷,简单的导出可以使用。

6:用XML做就很简单了。Word从2003开始支持XML格式,大致的思路是先用office2003或者2007编辑好word的样式,然后另存为xml,将xml翻译为FreeMarker模板,最后用java来解析FreeMarker模板并输出Doc。经测试这样方式生成的word文档完全符合office标准,样式、内容控制非常便利,打印也不会变形,生成的文档和office中编辑文档完全一样。

综合以上资料的参考,和网上的一些意见,最后我选择了,第6种用xml做导出方案。

开发流程

替换word模板

对照上面发的那个word格式,将** 替换成变量名称,比如用户名称用${userName}表示,全部替换后的格式如下图所示

在这里插入图片描述

导出xml格式并重命名ftl格式

将上图的word文件另存为xml格式,并且重命名为ftl结尾的文件

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

编写Java代码

项目需要引用到freemaker的jar包,在pom.xml配置文件中,添加下面的依赖

<!-- freemarker jar -->
		<dependency>
	     <groupId>org.freemarker</groupId>
	     <artifactId>freemarker</artifactId>
	     <version>2.3.20</version>
	 	</dependency>

将ftl模板文件复制到项目的指定路径下

将freemaker的配置文件放到项目的指定目录下

编写WordUtil.java

package com.xyq.maventest.util;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;

import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
/***
 * 
* Project Name:maventest
* <p>生成word工具类<br> 
*
* @ClassName: WordUtil
* @date 2019年2月20日  下午5:50:49
*
* @author youqiang.xiong
* @version 1.0
* @since  
* @see
 */
public class WordUtil {  
      
    private Configuration configuration = null;  
    
    /****
     * 模板文件存放的目录
     */
    private static final String  baseDir = "F:/study/GIT/maventest/src/main/resources/freemaker";
    /***
     * 模板文件名称
     */
    private static final String  templateFile = "template.ftl";
    /***
     * word生成的输出目录
     */
    private static final String  outputDir = "F:/file/";
      
    public WordUtil(){  
        configuration = new Configuration();  
        configuration.setDefaultEncoding("UTF-8");  
    }  
      
    public static void main(String[] args) {  
        WordUtil test = new WordUtil();  
        test.createWord();  
    }  
     
    /*****
     * 
    * Project Name: maventest
    * <p>转换成word<br> 
    *
    * @author youqiang.xiong
    * @date 2019年2月21日  上午11:22:03
    * @version v1.0
    * @since
     */
    public void createWord(){  
        Map<String,Object> dataMap=new HashMap<String,Object>();  
        //构造参数
        getData(dataMap);  
        
        configuration.setClassForTemplateLoading(this.getClass(), "");//模板文件所在路径
        Template t=null;  
        try {  
        	configuration.setDirectoryForTemplateLoading(new File(baseDir));
        	t = configuration.getTemplate(templateFile);
        } catch (IOException e) {  
            e.printStackTrace();  
        }  
        File outFile = new File(outputDir+Math.random()*10000+".doc"); //导出文件
        Writer out = null;  
        try {  
            out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile)));  
        } catch (FileNotFoundException e1) {  
            e1.printStackTrace();  
        }  
           
        try {  
            t.process(dataMap, out); //将填充数据填入模板文件并输出到目标文件 
            System.out.println("生成成功...");
        } catch (TemplateException e) {  
            e.printStackTrace();  
        } catch (IOException e) {  
            e.printStackTrace();  
        }  
    }  
  
    /****
     * 
    * Project Name: maventest
    * <p>初始化数据map <br> 
    *
    * @author youqiang.xiong
    * @date 2019年2月21日  上午11:26:34
    * @version v1.0
    * @since 
    * @param dataMap
    * 		封装数据的map
     */
    private void getData(Map<String, Object> dataMap) {  
        dataMap.put("userName", "刘德华");  
        dataMap.put("sex", "男");  
        dataMap.put("nation", "汉族");  
        dataMap.put("birthday", "1985-02-26");   
        dataMap.put("nationPlace", "春日部");  
        dataMap.put("politicalStatus", "党员");  
        dataMap.put("graduationSchool", "双叶幼稚园");  
        dataMap.put("lastBackground", "幼稚园");  
        dataMap.put("graduationMajor", "玩泥沙");  
        dataMap.put("workUnit", "NASA");  
        dataMap.put("business", "煮菜的");  
        dataMap.put("postalAddress", "lc");  
        dataMap.put("postalcode", "lc");  
        dataMap.put("mobile", "18898416969");  
        dataMap.put("admissionTicket", "lc");  
        dataMap.put("enterSchoolTime", "lc");  
        dataMap.put("emergencyContact", "lc");  
        dataMap.put("readingInstrouction", "lc");  
        dataMap.put("year", "2019");  
        dataMap.put("month", "02");  
        dataMap.put("day", "20");  
     
    }  
}

运行main方法,抛出如下异常:

freemarker.core.ParseException: Parsing error in template "template.ftl" in line 3, column 11570:
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:665)
	at com.xyq.maventest.util.WordUtil.createWord(WordUtil.java:75)
	at com.xyq.maventest.util.WordUtil.main(WordUtil.java:53)
Exception in thread "main" java.lang.NullPointerException
	at com.xyq.maventest.util.WordUtil.createWord(WordUtil.java:88)
	at com.xyq.maventest.util.WordUtil.main(WordUtil.java:53)

这个错误估计很人都有遇到过,起初我并不知道报错提示的是什么原因,大概猜测应该是ftl模板出错了,但是又不知道为什么会报错,初步怀疑是不是word编写的变量 格 式 书 写 有 误 ? 后 来 返 回 修 改 w o r d 的 变 量 格 式 逐 步 缩 小 问 题 原 因 所 在 , 经 过 反 复 试 验 才 发 现 , w o r d 转 换 成 x m l 格 式 的 时 候 , 如 果 文 档 中 有 {}格式书写有误? 后来返回修改word的变量格式逐步缩小问题原因所在,经过反复试验才发现,word转换成xml格式的时候,如果文档中有 wordwordxml{}特殊符号的时候,转换成xml格式会出现错位的情况,如下所示:
在这里插入图片描述

因为我对word生成xml这个机制并不是很了解,由于时间原因,也没有深究这里的根本原因,于是原因找到了,是生成ftl模板的时候格式有问题。那么既然${}符合会被解析错误的话,那可以换一种思路,将word中的符合全部不需要,只留变量即可,如下所示:

在这里插入图片描述

然后重新生成xml文件,修改文件后缀ftl,打开模板文件,将模板文件复制到notepad++文件编辑器打开,
然后一个个将变量替换成${}即可,比如讲userName 替换成 ${userName} 即可

在这里插入图片描述

一个个变量替换完毕后,将模板文件覆盖原有的内容,重新运行WordUtil.java ,运行成功。并且在指定目录下会生成一个新的word文件,打开word文件如下图所示:

在这里插入图片描述

至此用Java生成word的功能已经完成,大家可以根据各自所需修改word模板即可。

  • 14
    点赞
  • 75
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值