Freemarker+XML导出Word

   本来来自http://blog.csdn.net/xiexl/article/details/6747767;

 在Web应用中,有时需要按照固定的模板将数据导出到Word,如流程审批单,在流程处理完成后将处理过程按照流程单的要求导出,有时程序中需要实现生成标准Word文档,要求能够打印,并且保持页面样式不变,常见的方案有POI、iText、JACOB、JSP几种方式,POI读取Word文档比较适合、对于生成文档样式比较难控制,iText操作Excel还可以,对Word的操作功能有限,JACOB操作Word实现复杂,并且无法将服务部署到Linux平台,要求安装office,对于实现固定格式的报表实现困难,对于JSP直接输出方式样式控制难。

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

1、  首先用office【版本要2003以上,以下的不支持xml格式】编辑文档的样式,将需要动态填充的内容使用Freemarker标签替换:Word文档样式如下:

 

2、  将Word文档另存为XML格式,将后缀名“xml”修改为“ftl”

3、  使用Freemarker填充内容,代码如下:

 
  1. package com.test.freemarker.report;  
  2.   
  3.    
  4.   
  5. import java.io.BufferedWriter;  
  6.   
  7. import java.io.File;  
  8.   
  9. import java.io.FileOutputStream;  
  10.   
  11. import java.io.IOException;  
  12.   
  13. import java.io.OutputStreamWriter;  
  14.   
  15. import java.io.Writer;  
  16.   
  17. import java.util.HashMap;  
  18.   
  19. import java.util.Map;  
  20.   
  21.    
  22.   
  23. import freemarker.template.Configuration;  
  24.   
  25. import freemarker.template.Template;  
  26.   
  27. import freemarker.template.TemplateException;  
  28.   
  29.    
  30.   
  31. public class DocumentHandler {  
  32.   
  33.    private Configuration configuration = null;  
  34.   
  35.    
  36.   
  37.    public DocumentHandler() {  
  38.   
  39.       configuration = new Configuration();  
  40.   
  41.       configuration.setDefaultEncoding("utf-8");  
  42.   
  43.    }  
  44.   
  45.    
  46.   
  47.    public void createDoc() {  
  48.   
  49.       // 要填入模本的数据文件   
  50.   
  51.       Map dataMap = new HashMap();  
  52.   
  53.       getData(dataMap);  
  54.   
  55.       // 设置模本装置方法和路径,FreeMarker支持多种模板装载方法。可以重servlet,classpath,数据库装载,   
  56.   
  57.       // 这里我们的模板是放在com.havenliu.document.template包下面   
  58.   
  59.       configuration.setClassForTemplateLoading(this.getClass(),  
  60.   
  61.             "/com/test/freemarker/report");  
  62.   
  63.       Template t = null;  
  64.   
  65.       try {  
  66.   
  67.          // test.ftl为要装载的模板   
  68.   
  69.          t = configuration.getTemplate("test.ftl");  
  70.   
  71.          t.setEncoding("utf-8");  
  72.   
  73.       } catch (IOException e) {  
  74.   
  75.          e.printStackTrace();  
  76.   
  77.       }  
  78.   
  79.       // 输出文档路径及名称   
  80.   
  81.       File outFile = new File("D:/test.doc");  
  82.   
  83.       Writer out = null;  
  84.   
  85.       try {  
  86.   
  87.          out = new BufferedWriter(new OutputStreamWriter(  
  88.   
  89.                 new FileOutputStream(outFile), "utf-8"));  
  90.   
  91.    
  92.   
  93.       } catch (Exception e1) {  
  94.   
  95.          e1.printStackTrace();  
  96.   
  97.       }  
  98.   
  99.       try {  
  100.   
  101.          t.process(dataMap, out);  
  102.   
  103.          out.close();  
  104.   
  105.       } catch (TemplateException e) {  
  106.   
  107.          e.printStackTrace();  
  108.   
  109.       } catch (IOException e) {  
  110.   
  111.          e.printStackTrace();  
  112.   
  113.       }  
  114.   
  115.    }  
  116.   
  117.    
  118.   
  119.    /** 
  120.  
  121.     * 注意dataMap里存放的数据Key值要与模板中的参数相对应 
  122.  
  123.     *  
  124.  
  125.     * @param dataMap 
  126.  
  127.     */  
  128.   
  129.    private void getData(Map dataMap) {  
  130.   
  131.       dataMap.put("title_name""用户信息");  
  132.   
  133.       dataMap.put("user_name""张三");  
  134.   
  135.       dataMap.put("org_name""微软公司");  
  136.   
  137.       dataMap.put("dept_name""事业部");  
  138.   
  139.    }  
  140.   
  141. }  
package com.test.freemarker.report; import java.io.BufferedWriter; import java.io.File; 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; public class DocumentHandler { private Configuration configuration = null; public DocumentHandler() { configuration = new Configuration(); configuration.setDefaultEncoding("utf-8"); } public void createDoc() { // 要填入模本的数据文件 Map dataMap = new HashMap(); getData(dataMap); // 设置模本装置方法和路径,FreeMarker支持多种模板装载方法。可以重servlet,classpath,数据库装载, // 这里我们的模板是放在com.havenliu.document.template包下面 configuration.setClassForTemplateLoading(this.getClass(), "/com/test/freemarker/report"); Template t = null; try { // test.ftl为要装载的模板 t = configuration.getTemplate("test.ftl"); t.setEncoding("utf-8"); } catch (IOException e) { e.printStackTrace(); } // 输出文档路径及名称 File outFile = new File("D:/test.doc"); Writer out = null; try { out = new BufferedWriter(new OutputStreamWriter( new FileOutputStream(outFile), "utf-8")); } catch (Exception e1) { e1.printStackTrace(); } try { t.process(dataMap, out); out.close(); } catch (TemplateException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } /** * 注意dataMap里存放的数据Key值要与模板中的参数相对应 * * @param dataMap */ private void getData(Map dataMap) { dataMap.put("title_name", "用户信息"); dataMap.put("user_name", "张三"); dataMap.put("org_name", "微软公司"); dataMap.put("dept_name", "事业部"); } }
4、  生成的Word结果如下:

对于复杂的报表样式可以在Word中编辑后保存,如果需要输出列表类型数据可以参考Freemarker的循环或逻辑控制。