JAVA通过freemaker生成标准格式DOCX文件再转换为PDF格式

1.       生成docx模板和xml模板

 

生成docx模板

 

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

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

JAVA通过freemaker生成标准格式DOCX文件再转换为PDF格式_第1张图片

 

生成xml模板

 

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

 

JAVA通过freemaker生成标准格式DOCX文件再转换为PDF格式_第2张图片

 

 

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

 

JAVA通过freemaker生成标准格式DOCX文件再转换为PDF格式_第3张图片

 

 

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

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

 

生成的Xml模板:

 

[html] view plain copy

 

  1.   
  2.   
  3.   
  4.   
  5.   
  6.   
  7.   
  8.   
  9.   
  10.   
  11.   
  12.   
  13.   
  14.   
  15.   
  16.   
  17.   
  18.   
  19.   
  20. 这是一个测试Word  
  21.   
  22.   
  23.   
  24.   
  25.   
  26.   
  27.   
  28.   
  29.   
  30.   
  31.   
  32.   
  33.   
  34.   
  35.   
  36.   
  37.   
  38.   
  39.   
  40.   
  41.   
  42.   
  43.   
  44.   
  45.   
  46.   
  47.   
  48.   
  49.   
  50.   
  51.   
  52.   
  53.   
  54. ${test}  
  55.   
  56.   
  57.   
  58.   
  59.   
  60.   
  61.    
  62.   
  63.   
  64. <#list students as s>  
  65.   
  66.   
  67.   
  68.   
  69.   
  70.   
  71.   
  72.   
  73.   
  74.   
  75.   
  76.   
  77.   
  78.   
  79.   
  80.   
  81.   
  82. ${s}  
  83.   
  84.   
  85.   
  86.   
  87.   
  88.   
  89.   
  90.   
  91.   
  92.   
  93.   
  94.   

 

 

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 p = new HashMap();
List students = new ArrayList();
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 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文件的问题

测试调用的代码:

 

[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 p = new HashMap();  
  16.         List students = new ArrayList();  
  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依赖



    com.artofsolving
    jodconverter-maven-plugin
    2.2.1


转换工具:

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格式问题。
    

 

你可能感兴趣的:(JAVA通过freemaker生成标准格式DOCX文件再转换为PDF格式)