使用freemarker生成word

最近弄一个项目,需要读取xml文件里面的内容生成word文档,网上找了很多方案,最后选择用freemarker来处理,一是读写起来比较简单,而是不需要设置复杂的Word格式且一旦格式有变化重新生成模板就成,不需要改java代码。这篇文档有介绍怎么生成xml模板以及静态的例子,我也是参考这篇来处理的。链接:

http://blog.csdn.net/lun379292733/article/details/18673081

 

我这里写个简单的过程吧,加深下印象。

 

一、创建模板文件:

1、找到需要做成模板的word文档,将其中的关键数据用EL表达式来替换,譬如:

姓名:wjl

改成:

姓名:${name}

 

2、将替换成EL之后的word另存为xml(注意保存时选择“只保存数据”);

 

3、修改xml文件的后缀名,改成ftl格式。

 

4、我的word模板里面有个需要循环的列表,列表里面的内容我定义成了${content},但是这个内容有很多,类似于循环一样,因此需要添加<#list>来体现循环,如图:
使用freemarker生成word_第1张图片
其中,真正的标签是<#list>,fileList是要循环的list,file是别面,用<c:foreach>来说就是:fileList是放在item里面的东西,file类似于var里面的东西。

5、我的这个ftl文件名称为:word.ftl,放在config文件夹下(不是包,参考的那篇文章是放在包下的,我这个放在文件夹下)。
 
二、读取xml文件生成word:

1、createWordFile用来组装业务,包括读取xml数据生成word,并返回给调用出的结果。

/**
 * 该方法用来生成word文件
 * @param:父级文件夹名称
 * @param:需要处理的文件所在目录
 * */
public boolean createWordFile(String parentFileName,String newFilePath) throws Exception{
	boolean result=false;
	File xmlFile = new File(newFilePath+File.separator+"200105.xml");
	if(xmlFile.isFile() && xmlFile.exists()){//是文件且存在
		List list = readWord(xmlFile);//组装数据、以及文件名称
		if(list!=null && list.size()>0){
			String wordName = list.get(0).toString();//文件名称
			Map dataMap = (Map)list.get(1);//集合数据
			String templateFilePath = new File("").getAbsolutePath()+File.separator+"config";//设定为当前文件夹
//				System.out.println("模板文件所在目录:"+templateFilePath);
			boolean flag = createWord(dataMap,templateFilePath,"word.ftl",classFile.getAbsolutePath(),wordName+".doc");//生成word
			if(flag){
				result=true;
			}else{
				errorInfo = "word文件生成失败!";
			}
		}else{
			errorInfo="无法读取"+(newFilePath+File.separator+"200105.xml")+"文件中的数据信息!";
		}
	}
	return result;
}

 

2、readWord用来读取xml文件组装数据,并返回数据以及word文件名称(因为word文件名称来源于xml文件)。

/***
 * 该方法用来组装数据
 * @param xmlFile:需要进行解析的xml文件
 * */
public List readWord(File xmlFile) {
	/** 用于组装word页面需要的数据 */
    Map<String, Object> dataMap = new HashMap<String, Object>();
    List list = null;
    StringBuffer wordName=new StringBuffer();//word文档名称:案件编号+名称+内部编号
	try {
		//读取XML
        SAXReader sax = getSAXReader();
        Document doc = sax.read(xmlFile);//读取文档
        String fileContent="",appAJ="";
        //1、案件编号
        Node  node = doc.selectSingleNode("//ANJIANBH");
        if(node!=null ){
     	  fileContent=node.getText().trim();
         }else{
        	 fileContent="";
         }
        wordName.append(fileContent);
        dataMap.put("caseNum",fileContent);//案件编号
        
        //2、名称
        node = doc.selectSingleNode("//FAMINGCZMC");
        if(node!=null ){
     	  fileContent=node.getText().trim();
         }else{
        	 fileContent="";
         }
        wordName.append(escapeExprSpecialWord(fileContent));//去除特殊字符
        dataMap.put("name",fileContent);//名称
        
         //3、内部编号/申请号
        fileContent="内部编号:";
        node = doc.selectSingleNode("//NEIBUBH");
        if(node!=null ){//说明没有内部编号
        	fileContent="内部编号:"+node.getText().trim();
        	appAJ = node.getText().trim();
        	if(appAJ.toUpperCase().startsWith("AJ")){//避免出现小写aj的情况,所以全部转成大写进行比较
				//内部编号用最后一个下划线截取
				appAJ = appAJ.substring(appAJ.lastIndexOf("_")+1);
			}
	     	wordName.append(appAJ);
         }else{//那么查找是否有申请号
        	 node = doc.selectSingleNode("//SHENQINGH");
        	 if(node!=null ){
        		 fileContent=node.getText().trim();
        		 wordName.append(fileContent.split(":")[1]);
        	 }
         }
        dataMap.put("num",fileContent);
        
      //4、提交人姓名或名称
        node = doc.selectSingleNode("//XINGMINGHMC");
        if(node!=null ){
        	fileContent=node.getText().trim();
         }else{
        	 fileContent="";
         }
        dataMap.put("proxy",fileContent);
       //5、 收到时间
        node = doc.selectSingleNode("//QIANMINGSJC");
        if(node!=null ){
        	fileContent=node.getText().trim();
         }else{
        	 fileContent="";
         }
        dataMap.put("time",fileContent);//收到时间
        
        //6、收到文件情况
         StringBuffer content= new StringBuffer();
         Element element=null;
         List<Map<String, Object>> fileList=new ArrayList<Map<String,Object>>();
         List<Element> els = doc.selectNodes("//SHOUDAOWJ");
         if(els!=null && els.size()>0){
         	for(int i=0;i<els.size();i++){
         		Map<String, Object> map=new HashMap<String, Object>();
         		element = els.get(i);
         		content.delete(0,content.length());//清空
         		content.append((i+1)+"、");//序号
         		node = element.selectSingleNode(".//WENJIANMC");//文件名称 
         		content.append(node.getText().trim()+"          ");
         		node = element.selectSingleNode(".//WENJIANGS");//文件格式
         		content.append(node.getText().trim()+"格式    ");
         		node = element.selectSingleNode(".//WENJIANDX");//文件大小
         		content.append("文件大小"+node.getText().trim());
//	         		System.out.println(content.toString());
    	        map.put("content", content.toString());
    	        fileList.add(map);
         	}
         }else{
        	 Map<String, Object> map=new HashMap<String, Object>();
        	 map.put("content","");
    	     fileList.add(map);
         }
         dataMap.put("fileList",fileList);
         // 落款时间
         node = doc.selectSingleNode("//SHOUDAOSJ");
         if(node!=null ){
        	 fileContent = node.getText().trim();
          }else{
        	  fileContent="";
          }
         dataMap.put("date",fileContent);//落款时间
         list = new ArrayList();
         list.add(wordName);
         list.add(dataMap);
	} catch (Exception e) {
		e.printStackTrace();
		return null;
	}
	return list;
}

/**
 * 该方法用来获取SXAReader对象
 * */
public SAXReader getSAXReader(){
	SAXReader saxReader = new SAXReader();
	/* 在读取文件时,去掉dtd的验证,可以缩短运行时间  */
	try {
//		saxReader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);//可能需要网络,所以不用它
		saxReader.setFeature(Constants.XERCES_FEATURE_PREFIX + Constants.LOAD_EXTERNAL_DTD_FEATURE, false);  //设置不需要校验头文件
	} catch (SAXException e) {
		e.printStackTrace();
	}
	return saxReader;
}

/**
 * 转义正则特殊字符 ($()*+.[]?\^{},|/\:?"<>)
 * @param keyword
 * @return
 */
public String escapeExprSpecialWord(String keyword) {
	if(keyword!=null && keyword.trim().length()>0){
		String[] fbsArr = {"/","\\",":","*","?","\"","<",">","|", "(", ")","+", "[", "]", "^", "{", "}","、"};
		for (String key : fbsArr) {
			if (keyword.contains(key)) {
				keyword = keyword.replace(key,"");
			}
		}
	}
	return keyword;
}

 

3、createWord用来生成word文档。

/**
* 该方法用来生成word文件
* @param dataMap:数据集合
* @param templateFilePath:模板文件所在目录
* @param templateFileName:模板文件名称
* @param docFilePath:生成的doc文件保存路径
*@param docFileName:生成的doc文件名称:名称+.doc
*/
public static boolean createWord(Map dataMap,
		String templateFilePath,String templateFileName,
		String docFilePath,String docFileName){
        try {
        	Configuration configuration = new Configuration();//创建配置实例 
            configuration.setDefaultEncoding("UTF-8");//设置编码
            configuration.setDirectoryForTemplateLoading(new File(templateFilePath));//加载模板文件
            Template template = configuration.getTemplate(templateFileName);//获取模板
            if(docFileName==null || !docFileName.toUpperCase().endsWith(".DOC")){//为空或者不是以doc结尾的,添加后缀名
            	docFileName = docFileName+".doc";
            }
            File outFile = new File(docFilePath+File.separator+docFileName);
            //如果输出目标文件夹不存在,则创建
            if (!outFile.getParentFile().exists()){
                outFile.getParentFile().mkdirs();
            }
            //将模板和数据模型合并生成文件 
            Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile),"UTF-8"));
            //生成文件
            template.process(dataMap, out);
            //关闭流
            out.flush();
            out.close();
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
}

 

 

三、需要用到的jar包:

freemarker-2.3.16.jar:使用freemarker生成word的主要包
dom4j-1.6.1.jar:解析xml文件的主要包
jaxen-1.1-beta-7.jar:解析xml文件的辅助包
已经上传到附件,有需要的可以下载。
 

三、需要注意的问题:

a、将word另存为xml得到的xml文件格式很乱,可能只有几行,可以通过格式化工具格式化一下。这样子易读性是增强了,但是可能会影响word格式。我这里有个落款时间就是因为格式化后出现了问题。格式化之前落款时间是居右显示,格式化之后竟然居中了,代码没有变化,可能就是中间出现了空格导致了问题。所以格式化之后最好测试话一下,或者不要格式化。
 
b、得到的xml文件里的$与参数分开了。譬如原本的word里面写的参数为${content},xml之后${content}被拆成了${,content,和}三部分。具体原因和解决办法可以看看 参考文件,我这就是老出现这个问题,弄了好几次还这样子,不得已只能直接改xml文件。
 
c、put到map里的参数一定要与xml中写的的参数一致,否则会报错。特别是有list的情况下,别名一定不能忘。我的例子就是${file.content}这个file一定不能忘,否则也会报错。值得庆幸的是,报错很明显,一查就能查不出来。

你可能感兴趣的:(freemarker,word,SAXReader)