java导出2007版word(docx格式)freemarker + xml 实现

 

http://blog.csdn.net/yigehui12/article/details/52840121

 

Freemarker+xml生成docx

原理概述:word从2003版就支持xml格式,而freemarker是java封装的模板工具,两者结合也就是在xml中需要动态生成的部分调用freemarker的指令(类似于EL表达式),来生成我们需要的数据,再用流输出文件,就达到了写word的效果。

生成word的基本流程图如下:

1.       生成docx模板和xml模板

生成docx模板

 

按照项目需要生成固定格式的docx格式的模板。

为方便测试做了个简单的例子,docx模板的内容如下图:

生成xml模板

从docx模板中取出word/document.xml,由于docx属于zip格式,可以用winrar打开,如图:

除word文件夹外其它文件为基本配置文件,取出word/document.xml(存放word文件的文本内容)如下图:

需要把document.xml解压出来,修改里面需要从数据库导出的数据用freemarker的指令代替,例如${test} 同时可以用遍历函数

替换完成后相当于生成了xml模板

生成的Xml模板:

[html] view plain copy
 
  1. <?xml version="1.0" encoding="UTF-8" standalone="yes"?>  
  2.   
  3. <w:document xmlns:wpc="http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:wp14="http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" xmlns:wpg="http://schemas.microsoft.com/office/word/2010/wordprocessingGroup" xmlns:wpi="http://schemas.microsoft.com/office/word/2010/wordprocessingInk" xmlns:wne="http://schemas.microsoft.com/office/word/2006/wordml" xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape" mc:Ignorable="w14 wp14">  
  4. <w:body>  
  5. <w:p w:rsidR="009175C2" w:rsidRDefault="005B5FB3" w:rsidP="005B5FB3">  
  6. <w:pPr>  
  7. <w:pStyle w:val="1"/>  
  8. <w:jc w:val="center"/>  
  9. <w:rPr>  
  10. <w:rFonts w:hint="eastAsia"/>  
  11. </w:rPr>  
  12. </w:pPr>  
  13. <w:bookmarkStart w:id="0" w:name="_GoBack"/>  
  14. <w:bookmarkEnd w:id="0"/>  
  15. <w:r>  
  16. <w:rPr>  
  17. <w:rFonts w:hint="eastAsia"/>  
  18. </w:rPr>  
  19. <w:t>  
  20. 这是一个测试Word</w:t>  
  21. </w:r>  
  22. </w:p>  
  23. <w:p w:rsidR="005B5FB3" w:rsidRDefault="005B5FB3" w:rsidP="005B5FB3">  
  24. <w:pPr>  
  25. <w:rPr>  
  26. <w:rFonts w:hint="eastAsia"/>  
  27. </w:rPr>  
  28. </w:pPr>  
  29. </w:p>  
  30. <w:p w:rsidR="005B5FB3" w:rsidRDefault="005B5FB3" w:rsidP="005B5FB3">  
  31. <w:pPr>  
  32. <w:rPr>  
  33. <w:rFonts w:hint="eastAsia"/>  
  34. </w:rPr>  
  35. </w:pPr>  
  36. </w:p>  
  37. <w:p w:rsidR="005B5FB3" w:rsidRDefault="005B5FB3" w:rsidP="005B5FB3">  
  38. <w:pPr>  
  39. <w:pStyle w:val="a3"/>  
  40. <w:numPr>  
  41. <w:ilvl w:val="0"/>  
  42. <w:numId w:val="1"/>  
  43. </w:numPr>  
  44. <w:ind w:firstLineChars="0"/>  
  45. <w:rPr>  
  46. <w:rFonts w:hint="eastAsia"/>  
  47. </w:rPr>  
  48. </w:pPr>  
  49. <w:r>  
  50. <w:rPr>  
  51. <w:rFonts w:hint="eastAsia"/>  
  52. </w:rPr>  
  53. <w:t>  
  54. ${test}</w:t>  
  55. </w:r>  
  56. <w:r>  
  57. <w:rPr>  
  58. <w:rFonts w:hint="eastAsia"/>  
  59. </w:rPr>  
  60. <w:t xml:space="preserve">  
  61.  </w:t>  
  62. </w:r>  
  63. </w:p>  
  64. <#list students as s>  
  65. <w:p w:rsidR="005B5FB3" w:rsidRDefault="005B5FB3" w:rsidP="005B5FB3">  
  66. <w:pPr>  
  67. <w:pStyle w:val="a3"/>  
  68. <w:numPr>  
  69. <w:ilvl w:val="0"/>  
  70. <w:numId w:val="1"/>  
  71. </w:numPr>  
  72. <w:ind w:firstLineChars="0"/>  
  73. <w:rPr>  
  74. <w:rFonts w:hint="eastAsia"/>  
  75. </w:rPr>  
  76. </w:pPr>  
  77. <w:r>  
  78. <w:rPr>  
  79. <w:rFonts w:hint="eastAsia"/>  
  80. </w:rPr>  
  81. <w:t>  
  82. ${s}  
  83. </w:t>  
  84. </w:r>  
  85. </w:p>  
  86. </#list>  
  87. <w:sectPr w:rsidR="005B5FB3" w:rsidRPr="005B5FB3">  
  88. <w:pgSz w:w="11906" w:h="16838"/>  
  89. <w:pgMar w:top="1440" w:right="1800" w:bottom="1440" w:left="1800" w:header="851" w:footer="992" w:gutter="0"/>  
  90. <w:cols w:space="425"/>  
  91. <w:docGrid w:type="lines" w:linePitch="312"/>  
  92. </w:sectPr>  
  93. </w:body>  
  94. </w:document>  

2.       利用freemarker填充数据并生成word文件

这里把数据库中的数据放到map中,把map和xml模板交给freemarker来生成(填充完数据)的xml具体实现方法如下:

[java] view plain copy
 
  1. package com.hannet.yigehui;  
  2.   
  3. import java.io.File;  
  4. import java.io.FileOutputStream;  
  5. import java.io.IOException;  
  6. import java.io.OutputStreamWriter;  
  7. import java.io.Writer;  
  8. import java.util.HashMap;  
  9. import java.util.Map;  
  10.   
  11.   
  12. import freemarker.template.Configuration;  
  13. import freemarker.template.Template;  
  14. import freemarker.template.TemplateException;  
  15.   
  16. public class XmlToExcel {  
  17.       
  18.       
  19.     private static XmlToExcel tplm = null;  
  20.      private Configuration cfg = null;  
  21.   
  22.      private XmlToExcel() {  
  23.       cfg = new Configuration();  
  24.      try {  
  25.       //注册tmlplate的load路径  
  26.        cfg.setClassForTemplateLoading(this.getClass(), "/template/");  
  27.       } catch (Exception e) {  
  28.          
  29.       }  
  30.      }  
  31.   
  32.      private static Template getTemplate(String name) throws IOException {  
  33.       if(tplm == null) {  
  34.        tplm = new XmlToExcel();  
  35.       }  
  36.       return tplm.cfg.getTemplate(name);  
  37.      }  
  38.        
  39.      /** 
  40.       *  
  41.       * @param templatefile 模板文件 
  42.       * @param param        需要填充的内容 
  43.       * @param out          填充完成输出的文件 
  44.       * @throws IOException 
  45.       * @throws TemplateException 
  46.       */  
  47.      public static void process(String templatefile, Map param ,Writer out) throws IOException, TemplateException{  
  48.       //获取模板  
  49.       Template template=XmlToExcel.getTemplate(templatefile);  
  50.       template.setOutputEncoding("UTF-8");  
  51.       //合并数据  
  52.       template.process(param, out);  
  53.       if(out!=null){  
  54.             out.close();  
  55.         }  
  56.      }  
  57. }  

利用freemarker生成数据

调用freemarker中的process方法(上图中的方法)来填充数据。
例(用1中生成的xml为模板填充数据):


//xml的模板路径  template/test.xml
String xmlTemplate = "test.xml";
//填充完数据的临时xml
String xmlTemp = "d:\temp.xml";
Writer w = new FileWriter(new File(xmlTemp));

//1.需要动态传入的数据
Map<String,Object> p = new HashMap<String,Object>();
List<String> students = new ArrayList<String>();
students.add("张三");
students.add("李四");
students.add("王二");
p.put("test", "测试一下是否成功");
p.put("students", students);

//2.把map中的数据动态由freemarker传给xml
XmlToExcel.process(xmlTemplate, p, w);

 注:下文中会给出源码这里只是方便理解。

导出word文件

 

利用java的zipFile 和 ZipOutputStream 及zipFile.getInputStream() 来根据docx模板导出 (需要把word/document.xml文件替换成(填充完数据)的xml)具体操作流程如下:

[java] view plain copy
 
  1. package com.hannet.yigehui;  
  2.   
  3. import java.io.File;  
  4. import java.io.FileInputStream;  
  5. import java.io.FileNotFoundException;  
  6. import java.io.FileOutputStream;  
  7. import java.io.FileWriter;  
  8. import java.io.IOException;  
  9. import java.io.InputStream;  
  10. import java.io.Writer;  
  11. import java.net.URISyntaxException;  
  12. import java.util.ArrayList;  
  13. import java.util.Enumeration;  
  14. import java.util.HashMap;  
  15. import java.util.List;  
  16. import java.util.Map;  
  17. import java.util.zip.ZipEntry;  
  18. import java.util.zip.ZipException;  
  19. import java.util.zip.ZipFile;  
  20. import java.util.zip.ZipOutputStream;  
  21.   
  22. import freemarker.template.TemplateException;  
  23.   
  24. /** 
  25.  * 其实docx属于zip的一种,这里只需要操作word/document.xml中的数据,其他的数据不用动 
  26.  * @author yigehui 
  27.  * 
  28.  */  
  29. public class XmlToDocx {  
  30.       
  31.     /** 
  32.      *  
  33.      * @param documentFile  动态生成数据的docunment.xml文件 
  34.      * @param docxTemplate  docx的模板 
  35.      * @param toFileName    需要导出的文件路径 
  36.      * @throws ZipException 
  37.      * @throws IOException 
  38.      */  
  39.       
  40.     public  void outDocx(File documentFile,String docxTemplate,String toFilePath) throws ZipException, IOException {  
  41.       
  42.         try {  
  43.         String fileName = XmlToDocx.class.getClassLoader().getResource("").toURI().getPath()+docxTemplate;  
  44.               
  45.             File docxFile = new File(fileName);  
  46.             ZipFile zipFile = new ZipFile(docxFile);              
  47.             Enumeration<? extends ZipEntry> zipEntrys = zipFile.entries();  
  48.             ZipOutputStream zipout = new ZipOutputStream(new FileOutputStream(toFilePath));  
  49.             int len=-1;  
  50.             byte[] buffer=new byte[1024];  
  51.             while(zipEntrys.hasMoreElements()) {  
  52.                 ZipEntry next = zipEntrys.nextElement();  
  53.                 InputStream is = zipFile.getInputStream(next);  
  54.                 //把输入流的文件传到输出流中 如果是word/document.xml由我们输入  
  55.                 zipout.putNextEntry(new ZipEntry(next.toString()));  
  56.                 if("word/document.xml".equals(next.toString())){  
  57.                     //InputStream in = new FileInputStream(new File(XmlToDocx.class.getClassLoader().getResource("").toURI().getPath()+"template/test.xml"));  
  58.                     InputStream in = new FileInputStream(documentFile);  
  59.                     while((len = in.read(buffer))!=-1){  
  60.                         zipout.write(buffer,0,len);  
  61.                     }  
  62.                     in.close();  
  63.                 }else {  
  64.                     while((len = is.read(buffer))!=-1){  
  65.                         zipout.write(buffer,0,len);  
  66.                     }  
  67.                     is.close();  
  68.                 }         
  69.             }             
  70.             zipout.close();           
  71.         } catch (URISyntaxException e) {  
  72.             e.printStackTrace();  
  73.         }catch (FileNotFoundException e) {  
  74.             e.printStackTrace();  
  75.         }     
  76.     }  
  77. }  


这里输出的文件即为完整的docx格式的word文件

测试调用的代码:

[java] view plain copy
 
  1. public static void main(String[] args) throws IOException, TemplateException {  
  2.   
  3.         //xml的模板路径*/*  
  4.         String xmlTemplate = "test.xml";  
  5.           
  6.         //设置docx的模板路径 和文件名  
  7.         String docxTemplate = "template/test.docx";  
  8.         String toFilePath = "d:\test.docx";  
  9.           
  10.         //填充完数据的临时xml  
  11.         String xmlTemp = "d:\temp.xml";  
  12.         Writer w = new FileWriter(new File(xmlTemp));  
  13.           
  14.         //1.需要动态传入的数据  
  15.         Map<String,Object> p = new HashMap<String,Object>();  
  16.         List<String> students = new ArrayList<String>();  
  17.         students.add("张三");  
  18.         students.add("李四");  
  19.         students.add("王二");       
  20.         p.put("test", "测试一下是否成功");  
  21.         p.put("students", students);  
  22.           
  23.         //2.把map中的数据动态由freemarker传给xml  
  24.         XmlToExcel.process(xmlTemplate, p, w);  
  25.           
  26.         //3.把填充完成的xml写入到docx中  
  27.         XmlToDocx xtd = new XmlToDocx();  
  28.         xtd.outDocx(new File(xmlTemp), docxTemplate, toFilePath);  
  29.         }  


调用成功后生成的test.docx如下图:

注:

测试项目的目录结构:

需要引入的包:

链接:http://pan.baidu.com/s/1eS7JHia 密码:tlec

原文地址:https://www.cnblogs.com/xiaoL/p/7904433.html