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左右。

浙公网安备 33010602011771号