• 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

  • 相关阅读:
    2016.2.17文件夹选择框及文件选择框
    2016.1.22 利用LINQ实现DataSet内多张DataTable关联查询操作(目前未发现太大价值)
    2016.1.23 通过cmd在程序中执行sql脚本
    2016.1.19 DEV Express控件GirdControl使用
    2016.1.1 VS中宏的使用技巧点滴
    2015.12.24(圣诞节) 解决Oralce数据库将具有相同属性的多行合并为一行的简单方法多年想要wmsys.wm_concat
    2015.12.12 DataGridveiw中添加checkbox列
    2015.11.3 RichBox改变若干文本颜色
    2015.12.10 如何将一个工程彻底改名
    2015.9.2 文本框中获取当前位置的所在行和列
  • 原文地址:https://www.cnblogs.com/xiaoL/p/7904433.html
Copyright © 2020-2023  润新知