Java中模板(freemarker)方式导出Word后文件巨大,及xml word打开提示转换的解决办法
两种解决方式:
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左右。