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

时间:2023-03-09 00:31:30
java导出2007版word(docx格式)freemarker + xml 实现

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

Freemarker+xml生成docx

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

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

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

1.       生成docx模板和xml模板

生成docx模板

 

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

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

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

生成xml模板

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

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

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

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

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

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

生成的Xml模板:

  1. <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  2. <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">
  3. <w:body>
  4. <w:p w:rsidR="009175C2" w:rsidRDefault="005B5FB3" w:rsidP="005B5FB3">
  5. <w:pPr>
  6. <w:pStyle w:val="1"/>
  7. <w:jc w:val="center"/>
  8. <w:rPr>
  9. <w:rFonts w:hint="eastAsia"/>
  10. </w:rPr>
  11. </w:pPr>
  12. <w:bookmarkStart w:id="0" w:name="_GoBack"/>
  13. <w:bookmarkEnd w:id="0"/>
  14. <w:r>
  15. <w:rPr>
  16. <w:rFonts w:hint="eastAsia"/>
  17. </w:rPr>
  18. <w:t>
  19. 这是一个测试Word</w:t>
  20. </w:r>
  21. </w:p>
  22. <w:p w:rsidR="005B5FB3" w:rsidRDefault="005B5FB3" w:rsidP="005B5FB3">
  23. <w:pPr>
  24. <w:rPr>
  25. <w:rFonts w:hint="eastAsia"/>
  26. </w:rPr>
  27. </w:pPr>
  28. </w:p>
  29. <w:p w:rsidR="005B5FB3" w:rsidRDefault="005B5FB3" w:rsidP="005B5FB3">
  30. <w:pPr>
  31. <w:rPr>
  32. <w:rFonts w:hint="eastAsia"/>
  33. </w:rPr>
  34. </w:pPr>
  35. </w:p>
  36. <w:p w:rsidR="005B5FB3" w:rsidRDefault="005B5FB3" w:rsidP="005B5FB3">
  37. <w:pPr>
  38. <w:pStyle w:val="a3"/>
  39. <w:numPr>
  40. <w:ilvl w:val="0"/>
  41. <w:numId w:val="1"/>
  42. </w:numPr>
  43. <w:ind w:firstLineChars="0"/>
  44. <w:rPr>
  45. <w:rFonts w:hint="eastAsia"/>
  46. </w:rPr>
  47. </w:pPr>
  48. <w:r>
  49. <w:rPr>
  50. <w:rFonts w:hint="eastAsia"/>
  51. </w:rPr>
  52. <w:t>
  53. ${test}</w:t>
  54. </w:r>
  55. <w:r>
  56. <w:rPr>
  57. <w:rFonts w:hint="eastAsia"/>
  58. </w:rPr>
  59. <w:t xml:space="preserve">
  60. </w:t>
  61. </w:r>
  62. </w:p>
  63. <#list students as s>
  64. <w:p w:rsidR="005B5FB3" w:rsidRDefault="005B5FB3" w:rsidP="005B5FB3">
  65. <w:pPr>
  66. <w:pStyle w:val="a3"/>
  67. <w:numPr>
  68. <w:ilvl w:val="0"/>
  69. <w:numId w:val="1"/>
  70. </w:numPr>
  71. <w:ind w:firstLineChars="0"/>
  72. <w:rPr>
  73. <w:rFonts w:hint="eastAsia"/>
  74. </w:rPr>
  75. </w:pPr>
  76. <w:r>
  77. <w:rPr>
  78. <w:rFonts w:hint="eastAsia"/>
  79. </w:rPr>
  80. <w:t>
  81. ${s}
  82. </w:t>
  83. </w:r>
  84. </w:p>
  85. </#list>
  86. <w:sectPr w:rsidR="005B5FB3" w:rsidRPr="005B5FB3">
  87. <w:pgSz w:w="11906" w:h="16838"/>
  88. <w:pgMar w:top="1440" w:right="1800" w:bottom="1440" w:left="1800" w:header="851" w:footer="992" w:gutter="0"/>
  89. <w:cols w:space="425"/>
  90. <w:docGrid w:type="lines" w:linePitch="312"/>
  91. </w:sectPr>
  92. </w:body>
  93. </w:document>

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

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

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

利用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)具体操作流程如下:

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

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

测试调用的代码:

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

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

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

注:

测试项目的目录结构:

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

需要引入的包:

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

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