1.       生成docx模板和xml模板

 

生成docx模板


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

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

javafx文档 javafx doc_javafx文档

 

生成xml模板

 

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

 

javafx文档 javafx doc_javafx文档_02

 

 

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

 

javafx文档 javafx doc_java_03

 

 

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

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

 

生成的Xml模板:

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具体实现方法如下:

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

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文件

//经过本人测试,也可直接将输出文件取.doc后缀,也可以打开与转换,且解决低版本jodconverter无法转换DOCX文件的问题

测试调用的代码:

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);

 

转换成PDF

1)基于openoffice(跨平台、需安装openoffice、复杂格式有错位)
原理:
通过第三方工具openoffice,将word、excel、ppt、txt等文件转换为pdf文件

先安装openoffice软件(Windows或Linux有提供软件)

使用JODConverter的Java的OpenDocument 文件转换器API操作Office系列文件转换为PDF文件


优点:
转换效果比较好。是比较主流的做法

缺点:
服务器需要安装openoffice,比较负重

具体实现:
1.下载安装软件
1)Openoffice:Apache下的一个开放免费的文字处理软件

下载地址:http://www.openoffice.org/zh-cn/download/

2)JODConverter一个Java的OpenDocument 文件转换器,只用到它的jar包

下载地址:https://sourceforge.net/projects/jodconverter/files/JODConverter/


2.启动服务:
打开dos窗口,进入openoffice安装盘符,输入以下代码来启动服务:

soffice -headless -accept="socket,host=127.0.0.1,port=8100;urp;" -nofirststartwizard


3.Java实现操作转化:
Pom.xml依赖

<!-- https://mvnrepository.com/artifact/com.artofsolving/jodconverter-maven-plugin -->
 <dependency>
     <groupId>com.artofsolving</groupId>
     <artifactId>jodconverter-maven-plugin</artifactId>
     <version>2.2.1</version></dependency>
 </dependency>


转换工具:

import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.ConnectException;
 import java.text.SimpleDateFormat;
 import java.util.Date; 
 import com.artofsolving.jodconverter.DocumentConverter;
 import com.artofsolving.jodconverter.openoffice.connection.OpenOfficeConnection;
 import com.artofsolving.jodconverter.openoffice.connection.SocketOpenOfficeConnection;
 import com.artofsolving.jodconverter.openoffice.converter.OpenOfficeDocumentConverter;
 /**
  * 利用jodconverter(基于OpenOffice服务)将文件(*.doc、*.docx、*.xls、*.ppt)转化为html格式或者pdf格式,
  * 使用前请检查OpenOffice服务是否已经开启, OpenOffice进程名称:soffice.exe | soffice.bin
  */
 public class Doc2HtmlUtil {
     private static Doc2HtmlUtil doc2HtmlUtil;
     /** * 获取Doc2HtmlUtil实例 */
     public static synchronized Doc2HtmlUtil getDoc2HtmlUtilInstance() {
         if (doc2HtmlUtil == null) {
             doc2HtmlUtil = new Doc2HtmlUtil();
         }
         return doc2HtmlUtil;
     }
     /*** 转换文件成pdf */
     public String file2pdf(InputStream fromFileInputStream, String toFilePath,String type) throws IOException {
         Date date = new Date();
         SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
         String timesuffix = sdf.format(date);
         String docFileName = null;
         String htmFileName = null;
         if(".doc".equals(type)){
             docFileName = "doc_" + timesuffix + ".doc";
             htmFileName = "doc_" + timesuffix + ".pdf";
         }else if(".docx".equals(type)){
             docFileName = "docx_" + timesuffix + ".docx";
             htmFileName = "docx_" + timesuffix + ".pdf";
         }else if(".xls".equals(type)){
             docFileName = "xls_" + timesuffix + ".xls";
             htmFileName = "xls_" + timesuffix + ".pdf";
         }else if(".ppt".equals(type)){
             docFileName = "ppt_" + timesuffix + ".ppt";
             htmFileName = "ppt_" + timesuffix + ".pdf";
         }else{
             return null;
         } 
         File htmlOutputFile = new File(toFilePath + File.separatorChar + htmFileName);
         File docInputFile = new File(toFilePath + File.separatorChar + docFileName);
         if (htmlOutputFile.exists())
             htmlOutputFile.delete();
         htmlOutputFile.createNewFile();
         if (docInputFile.exists())
             docInputFile.delete();
         docInputFile.createNewFile();
         /*** 由fromFileInputStream构建输入文件  */
         try {
             OutputStream os = new FileOutputStream(docInputFile);
             int bytesRead = 0;
             byte[] buffer = new byte[1024 * 8];
             while ((bytesRead = fromFileInputStream.read(buffer)) != -1) {
                 os.write(buffer, 0, bytesRead);
             } 
             os.close();
             fromFileInputStream.close();
         } catch (IOException e) {
         } 
         // 连接服务
         OpenOfficeConnection connection = new SocketOpenOfficeConnection(8100);
         try {
             connection.connect();
         } catch (ConnectException e) {
             System.err.println("文件转换出错,请检查OpenOffice服务是否启动。");
         }
         // convert 转换
         DocumentConverter converter = new OpenOfficeDocumentConverter(connection);
         converter.convert(docInputFile, htmlOutputFile);
         connection.disconnect();
         // 转换完之后删除word文件
         docInputFile.delete();                                                                
         return htmFileName;
     } 
     public static void main(String[] args) throws IOException {
         Doc2HtmlUtil coc2HtmlUtil = getDoc2HtmlUtilInstance ();
         File file = null;
         FileInputStream fileInputStream = null;
         file = new File("C:/Users/MACHENIKE/Desktop/xxx.doc");
         fileInputStream = new FileInputStream(file);
         coc2HtmlUtil.file2pdf(fileInputStream, "E:/360","doc");
     }
 }


简易实现:

public void createPdf(String docFileName) throws IOException{ 
         String path =  this.getSession().getServletContext().getRealPath("/")+"attachment/";
         File inputFile = new File(path+"/doc/"+ docFileName + ".doc");
         File outputFile = new File(path+"/pdf/"+docFileName + ".pdf");
          
         // connect to an OpenOffice.org instance running on port 8100
         OpenOfficeConnection connection = new SocketOpenOfficeConnection(8100);
         connection.connect();
          
         // convert
         DocumentConverter converter = new OpenOfficeDocumentConverter(connection);
         converter.convert(inputFile, outputFile);
          
         // close the connection
         connection.disconnect();

   本文由各个大神的博客总结,加上自身测试而成,可解决Freemaker生成的文档手机端不能打开,与转换成的PDF文档是XML格式问题。