兩種解決方式:
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左右。
