兩種解決方式:
Aspose 方式優點是速度快。缺點是收費的 且 格式不一定很好
jacob 方式優點是免費且樣式保持的相當好。缺點是特別慢而且只支持windows環境下
首先freemarker 導出word文檔直接參考:
https://juejin.im/post/6844903766970335246
jacob 集成參考:
https://blog.csdn.net/dangerous_fire/article/details/61922656
jacob 1.8下載地址
鏈接:https://pan.baidu.com/s/1rfymOlEKptyedtyDDVipqg 提取碼:50jp
Jacob的包pom引入
<dependency> <!-- jsoup HTML parser library @ https://jsoup.org/ --> <groupId>org.jsoup</groupId> <artifactId>jsoup</artifactId> <version>1.13.1</version> </dependency>
aspose方式直接引入(aspose-words-19.5-jdk.jar)jar包,由於是收費的我就不放出jar包了。(CSDN一堆,我也不是坑大家分畢竟我也沒放下載鏈接不是)
工具類JacobVariant:
package com.tpcp.topic.utils; public enum JacobVariant { // Variant(0):doc // *Variant(1):dot // *Variant(2-5),Variant(7):txt // *Variant(6):rft // *Variant(8),Variant(10):htm // *Variant(9):mht // *Variant(11),Variant(19-22):xml // *Variant(12):docx // *Variant(13):docm // *Variant(14):dotx // *Variant(15):dotm // *Variant(16)、Variant(24):docx // *Variant(17):pdf // *Variant(18):xps // *Variant(23):odt // *Variant(25):與Office2003與2007的轉換程序相關,執行本程序后彈出一個警告框說是需要更高版本的 Microsoft Works Converter /** * DOC */ DOC(0), /** * TXT */ TXT(7), /** * XML */ XML(11), /** * DOCX */ DOCX(12), /** * PDF */ PDF(17); private JacobVariant(int code) { this.code=code; } private int code; public int getCode() { return code; } public void setCode(int code) { this.code = code; } }
工具類JacobUtil:
package com.tpcp.topic.utils; import java.io.File; import com.aspose.words.Document; import com.jacob.activeX.ActiveXComponent; import com.jacob.com.Dispatch; import com.jacob.com.Variant; public class JacobUtil { /** * (jacob)將目標路徑文件轉換為指定的文件類型 * @param oldFilePath 要轉換的文件路徑 * @param newFileName 轉換后的文件名 * @param jacobVariant 要轉換的目標類型 * @param oldFileDel 轉換完成后轉換前的文件需要刪除嗎 * @return 轉換后的文件全路徑 */ public static String convertXMLWordToBaseWord(String oldFilePath,String newFileName,JacobVariant jacobVariant,Boolean oldFileDel) { long startTime = System.currentTimeMillis(); //獲取開始時間 //指定被轉換文件的完整路徑 String path = new String(oldFilePath); //根據路徑創建文件對象 File docFile=new File(path); //獲取文件名(包含擴展名) String filename=docFile.getName(); //設置輸出路徑,一定要包含輸出文件名(不含輸出文件的擴展名) String savepath = new String (System.getProperty("java.io.tmpdir")+File.separator+newFileName); //啟動Word程序 ActiveXComponent app = new ActiveXComponent("Word.Application"); //接收輸入文件和輸出文件的路徑 String inFile = path; String tpFile = savepath; //設置word不可見 app.setProperty("Visible", new Variant(false)); //這句不懂 Object docs = app.getProperty("Documents").toDispatch(); //打開輸入的doc文檔 Object doc = Dispatch.invoke((Dispatch) docs,"Open", Dispatch.Method, new Object[]{inFile,new Variant(false), new Variant(true)}, new int[1]).toDispatch(); //另存文件, 其中Variant(n)參數指定另存為的文件類型,詳見代碼結束后的文字 Dispatch.invoke((Dispatch) doc,"SaveAs", Dispatch.Method, new Object[]{tpFile,new Variant(jacobVariant.getCode())}, new int[1]); //這句也不懂 Variant f = new Variant(false); //關閉並退出 Dispatch.call((Dispatch) doc, "Close", f); app.invoke("Quit", new Variant[] {}); if(oldFileDel){ docFile.delete(); } System.out.println(tpFile+"."+jacobVariant+"轉換完畢。"); long endTime = System.currentTimeMillis(); //獲取結束時間 System.out.println("轉換用時:" + (endTime - startTime) + "ms"); return tpFile+"."+jacobVariant; } /** * 將目標路徑DOC 文件轉換為 指定路徑指定后綴文件 * @param oldFilePath 源文件路徑 * @param newFileName 新文件路徑 * @param oldFileDel 轉換完成后需要刪除源文件嗎 */ public static void convertXMLWordToBaseWord(String oldFilePath,String newFilePath,Boolean oldFileDel) { long startTime = System.currentTimeMillis(); //獲取開始時間 try { Document doc = new Document(oldFilePath); doc.save(newFilePath); } catch (Exception e) { e.printStackTrace(); } if(oldFileDel){ new File(oldFilePath).delete(); } long endTime = System.currentTimeMillis(); //獲取結束時間 System.out.println("轉換用時:" + (endTime - startTime) + "ms"); } public static void main(String[] args) { long startTime = System.currentTimeMillis(); //獲取開始時間 try { Document doc = new Document("E:\\upFiles\\123.doc"); doc.save("E:\\upFiles\\456.doc"); } catch (Exception e) { e.printStackTrace(); } long endTime = System.currentTimeMillis(); //獲取結束時間 System.out.println("轉換用時:" + (endTime - startTime) + "ms"); } }
工具類ExportWordUtil:
package com.tpcp.topic.utils; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; import java.io.Writer; import java.util.List; import java.util.Map; import javax.servlet.http.HttpServletResponse; import freemarker.template.Configuration; import freemarker.template.TemplateException; /** * 導出Word * @author ll * */ public class ExportWordUtil { /** * 導出Word並使用aspose.words 轉換為正常word * @param map 數據源 * @param ftlFloder 模板所在文件夾 例: request.getServletContext().getRealPath("/")+"export/template/" * @param ftlName 模板名稱 例: ZZBAB.ftl * @param exportWordName 要導出的文件名 例 : 課程備案表.doc * @param response HttpServletResponse * @param aspose true aspose方式(aspose方式耗時短 但是格式不一定能保持的很好) false Jacob方式(Jacob方式耗時長格式保持的相當好) */ public static void exportWordDataForMap(Map<String,Object> map,String ftlFloder,String ftlName,String exportWordName,HttpServletResponse response,boolean aspose) { try { String outFilePath = System.getProperty("java.io.tmpdir")+File.separator+UUIDGenerator.generate()+exportWordName; //創建配置實例 Configuration configuration = new Configuration(); configuration.setDefaultEncoding("UTF-8"); configuration.setDirectoryForTemplateLoading(new File(ftlFloder)); //獲取模板 freemarker.template.Template template = configuration.getTemplate(ftlName); //輸出文件 File outFile = new File(outFilePath); //將模板和數據模型合並生成文件 Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile),"UTF-8")); //生成文件 template.process(map, out); //關閉流 out.flush(); out.close(); exportWord(outFilePath,exportWordName,response,aspose); } catch (TemplateException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } /** * 導出Word * @param map 數據源 * @param ftlFloder 模板所在文件夾 例: request.getServletContext().getRealPath("/")+"export/template/" * @param ftlName 模板名稱 例: ZZBAB.ftl * @param exportWordName 要導出的文件名 例 : 課程備案表.doc * @param response HttpServletResponse * @param aspose true aspose方式(aspose方式耗時短 但是格式不一定能保持的很好) false Jacob方式(Jacob方式耗時長格式保持的相當好) */ public static void exportWordDataForList(List<Map<String,Object>> list,String ftlFloder,String ftlName,String exportWordName,HttpServletResponse response,boolean aspose) { try { String outFilePath = System.getProperty("java.io.tmpdir")+File.separator+UUIDGenerator.generate()+exportWordName; //創建配置實例 Configuration configuration = new Configuration(); configuration.setDefaultEncoding("UTF-8"); configuration.setDirectoryForTemplateLoading(new File(ftlFloder)); //獲取模板 freemarker.template.Template template = configuration.getTemplate(ftlName); //輸出文件 File outFile = new File(outFilePath); //將模板和數據模型合並生成文件 Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile),"UTF-8")); //生成文件 template.process(list, out); //關閉流 out.flush(); out.close(); exportWord(outFilePath,exportWordName,response,aspose); } catch (TemplateException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } public static void exportWord(String outFilePath,String filename,HttpServletResponse response,boolean aspose) { try{ String newFilePath = System.getProperty("java.io.tmpdir")+File.separator+UUIDGenerator.generate()+".doc"; if(aspose){ //aspose 轉換 JacobUtil.convertXMLWordToBaseWord(outFilePath, newFilePath, true); }else{//Jacob轉換 newFilePath = JacobUtil.convertXMLWordToBaseWord(outFilePath, UUIDGenerator.generate(), JacobVariant.DOC,true); } // 以流的形式下載文件。 File file = new File(newFilePath); InputStream fis = new BufferedInputStream(new FileInputStream(newFilePath)); byte[] buffer = new byte[fis.available()]; fis.read(buffer); fis.close(); // 清空response response.reset(); // 設置response的Header response.setHeader("Content-Disposition", "attachment;fileName=" + new String((filename).getBytes(), "iso-8859-1")); response.addHeader("Content-Length", "" + file.length()); OutputStream toClient = new BufferedOutputStream(response.getOutputStream()); response.setContentType("application/octet-stream"); toClient.write(buffer); toClient.flush(); toClient.close(); file.delete(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }
使用數據源為map的方法:
ExportWordUtil.exportWordDataForMap(map, request.getServletContext().getRealPath("/")+"export/template/", "ZZBAB.ftl", "xxx.doc", response, false);
Map數據源是單獨數據,list方式則是需要循環生成,這都取決於你的ftl模板。
還有一點需要注意的是你的ftl模板把它格式化成一行,這也有助於你的xml word文件大小減小。
最后附一張xml word轉為正常word文件后的文件大小對比:
可以看到xml word大小35M左右 轉換后只有6M左右。