Java使用FreeMarker來實(shí)現(xiàn)Word自定義導(dǎo)出功能
前言
在對一些特定導(dǎo)出功能,使用常規(guī)Excel無法解決的,通常使用Word來實(shí)現(xiàn)導(dǎo)出功能,這篇介紹下如何在Java中使用FreeMarker模板注入方式來實(shí)現(xiàn)Word導(dǎo)出功能
導(dǎo)出案例(已作打碼處理)

或者:

準(zhǔn)備工作
第一步:maven依賴庫
<!-- freemarker (用于Word導(dǎo)出)-->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>${freemarker.version}</version>
</dependency>
第二步:Word導(dǎo)出工具類
import com.zrxt.common.config.RuoYiConfig;
import com.zrxt.common.core.text.CharsetKit;
import freemarker.cache.ClassTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Map;
/**
* @ClassName WordUtil
* @Description 使用Freemarker生成Word文檔工具類
* @Author
* @Date 2023/12/14
**/
public class WordUtil {
/**
* 使用Freemarker自動生成Word文檔(磁盤路徑方法)
*
* @param dataMap 保存Word文檔中所需要的數(shù)據(jù)
* @param templatePath 模板文件的絕對路徑
* @param templateFile 模板文件的名稱
* @throws Exception
*/
public static void CreateWord(HttpServletResponse response, Map<String, Object> dataMap, String templatePath, String templateFile) throws Exception {
Writer out = null;
try {
// 設(shè)置FreeMarker的版本
Configuration configuration = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
// 設(shè)置Freemarker的編碼格式
configuration.setDefaultEncoding(CharsetKit.UTF_8);
// 設(shè)置FreeMarker生成Word文檔所需要的模板的路徑
configuration.setDirectoryForTemplateLoading(new File(templatePath));
// 設(shè)置FreeMarker生成Word文檔所需要的模板名稱
Template t = configuration.getTemplate(templateFile, CharsetKit.UTF_8);
// 創(chuàng)建一個(gè)Word文檔的輸出流
out = new BufferedWriter(new OutputStreamWriter(response.getOutputStream(), StandardCharsets.UTF_8));
//FreeMarker使用Word模板和數(shù)據(jù)生成Word文檔
t.process(dataMap, out);
}catch (Exception e){
e.printStackTrace();
}finally {
assert out != null;
out.flush();
out.close();
}
}
/**
* 使用Freemarker自動生成Word文檔
*
* @param dataMap 保存Word文檔中所需要的數(shù)據(jù)
* @param templatePath 模板文件的路徑(絕對)
* @param templateFile 模板文件的名稱
* @throws Exception
*/
public static void GeneratorWord(HttpServletResponse response, Map<String, Object> dataMap, String templatePath, String templateFile) throws Exception {
Writer out = null;
try {
// 設(shè)置FreeMarker的版本
Configuration configuration = new Configuration(Configuration.VERSION_2_3_28);
// 設(shè)置Freemarker的編碼格式
configuration.setDefaultEncoding(CharsetKit.UTF_8);
//相對路徑加載模板方法
configuration.setTemplateLoader(new ClassTemplateLoader(WordUtil.class,templatePath));
// 設(shè)置FreeMarker生成Word文檔所需要的模板名稱
Template template = configuration.getTemplate(templateFile, CharsetKit.UTF_8);
// 創(chuàng)建一個(gè)Word文檔的輸出流
out = new BufferedWriter(new OutputStreamWriter(response.getOutputStream(), StandardCharsets.UTF_8));
//FreeMarker使用Word模板和數(shù)據(jù)生成Word文檔
template .process(dataMap, out);
}catch (Exception e){
e.printStackTrace();
}finally {
assert out != null;
out.flush();
out.close();
}
}
/**
* 下載文件
*
* @param path 文件的位置
* @param fileName 自定義下載文件的名稱
* @param response http響應(yīng)
* @param request http請求
*/
public static void downloadFile(String path, String fileName, HttpServletResponse response, HttpServletRequest request) {
try {
File file = new File(path);
// 中文亂碼解決
String type = request.getHeader("User-Agent").toLowerCase();
if (type.indexOf("firefox") > 0 || type.indexOf("chrome") > 0) {
// 谷歌或火狐
fileName = new String(fileName.getBytes(StandardCharsets.UTF_8), "iso8859-1");
} else {
// IE
fileName = URLEncoder.encode(fileName, CharsetKit.UTF_8);
}
// 設(shè)置響應(yīng)的頭部信息
response.setHeader("content-disposition", "attachment;filename=" + fileName);
// 設(shè)置響應(yīng)內(nèi)容的類型
response.setContentType(getFileContentType(fileName) + "; charset=" + CharsetKit.UTF_8);
// 設(shè)置響應(yīng)內(nèi)容的長度
response.setContentLength((int) file.length());
// 輸出
outStream(new FileInputStream(file), response.getOutputStream());
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 文件的內(nèi)容類型
*/
private static String getFileContentType(String name) {
String result = "";
String fileType = name.toLowerCase();
if (fileType.endsWith(".png")) {
result = "image/png";
} else if (fileType.endsWith(".gif")) {
result = "image/gif";
} else if (fileType.endsWith(".jpg") || fileType.endsWith(".jpeg")) {
result = "image/jpeg";
} else if (fileType.endsWith(".svg")) {
result = "image/svg+xml";
} else if (fileType.endsWith(".doc")) {
result = "application/msword";
} else if (fileType.endsWith(".xls")) {
result = "application/x-excel";
} else if (fileType.endsWith(".zip")) {
result = "application/zip";
} else if (fileType.endsWith(".pdf")) {
result = "application/pdf";
} else {
result = "application/octet-stream";
}
return result;
}
/**
* 基礎(chǔ)字節(jié)數(shù)組輸出
*/
private static void outStream(InputStream is, OutputStream os) {
try {
byte[] buffer = new byte[10240];
int length = -1;
while ((length = is.read(buffer)) != -1) {
os.write(buffer, 0, length);
os.flush();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
os.close();
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 檢查存儲生成文件的路徑是否存在,如果不存在則新建路徑.
*
* @param directory the directory name, like '\dir-name'
*/
public static void CheckDownloadPath(String directory) {
File path = new File(RuoYiConfig.getDownloadPath() + directory);
if (!path.exists()) {
path.mkdirs();
}
}
}
模板準(zhǔn)備
第一步:首先要編寫Word模板,可以參考文章開頭的示例圖片。
第二步:Word模板編寫好后,點(diǎn)擊另存為,然后選擇Word 2003 XML文檔

第三步:保存好后的文件打開,Ctrl+A復(fù)制文檔所有內(nèi)容,然后在線搜索XML格式化在線工具,我這邊提供一個(gè)現(xiàn)成的XML格式化在線工具,將內(nèi)容全部粘貼進(jìn)去后點(diǎn)擊格式化按鈕。

創(chuàng)建ftl文件
上面所有工作準(zhǔn)備好后,就可以在resource目錄下創(chuàng)建一個(gè).ftl文件,然后將格式化后的代碼復(fù)制到文件中即可。

測試導(dǎo)出是否完整
在Controller層編寫測試代碼,判斷是否可以正常導(dǎo)出
/**
* 導(dǎo)出(word)關(guān)鍵過程控制詳細(xì)信息詳細(xì)信息(xml版本)
*/
@PostMapping(value = "/exportWord")
public void getInfo(MakeCriticalProcessControl param, HttpServletResponse response) {
try {
MakeCriticalProcessControl obj=
makeCriticalProcessControlService.selectById(param.getId());
Map<String, Object> context = new HashMap<>();
context.put("obj", obj);
WordUtil.GeneratorWord(response, context, "/wordDocumentFtl/make/", "MakeCriticalProcessControl.ftl");
} catch (Exception e) {
e.printStackTrace();
throw new ServiceException("導(dǎo)出Word文件失敗,請稍后重試!");
}
}
WordUtil.GeneratorWord()方法
第二個(gè)參數(shù)context為數(shù)據(jù)庫的數(shù)據(jù),要往word中空白單元格所填充的。
第三個(gè)參數(shù)為templatePath – 模板文件的路徑(相對路徑)。
第四個(gè)參數(shù)為具體的模板的文件名稱(帶后綴)
注:測試后需檢查格式是否混亂,是否有缺少單元格等情況,如果有則檢查原Word模板并修復(fù)然后重新走一遍流程即可
如何將數(shù)據(jù)庫數(shù)據(jù)填充到Word中
ftl文件中的內(nèi)容是一行一行的順序,也就是從第一行的第一個(gè)單元格開始,然后是第一行第二個(gè)單元格,第三個(gè)單元格…
然后第一行如果結(jié)束了,則是第二行第一個(gè)單元格、第二個(gè)單元格、第三個(gè)…
以此類推,所以優(yōu)先找到對應(yīng)標(biāo)題的對應(yīng)數(shù)據(jù)單元格在ftl文件的哪個(gè)位置。
如果是單個(gè)屬性或?qū)ο箢愋偷?,可以在controller中使用Map<String, Object> context = new HashMap<>();將數(shù)據(jù)put,然后key作為鍵,在ftl文件中可以把屬性獲取出來。例如下圖


如果涉及到遍歷,將一個(gè)list結(jié)果導(dǎo)出到word,請看以下案例
如果是遍歷導(dǎo)出的話,則在word模板設(shè)置時(shí),只需要設(shè)置一行表頭,下方對應(yīng)一行空的單元格即可,我們要循環(huán)空的單元格,然后將數(shù)據(jù)挨個(gè)寫入。

只需要在標(biāo)題下寫一行即可
在controller中依舊是查詢出對應(yīng)list,然后put到map中


1:判斷l(xiāng)ist是否為空
2:循環(huán)遍歷list
3:一般<w:tr>表示為行,所以這里的意思就是,list有多少數(shù)據(jù),則就生成多少行
如何要獲取值的話就可以${item.屬性名!}

如果涉及到If語句或者是需要將數(shù)字轉(zhuǎn)為對應(yīng)的狀態(tài)

以上就是Java使用FreeMarker來實(shí)現(xiàn)Word自定義導(dǎo)出功能的詳細(xì)內(nèi)容,更多關(guān)于Java FreeMarker Word自定義導(dǎo)出的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Spring Data JPA中的動態(tài)查詢實(shí)例
本篇文章主要介紹了詳解Spring Data JPA中的動態(tài)查詢。小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-04-04
SpringBoot?整合mapstruct的實(shí)現(xiàn)步驟
這篇文章主要介紹了SpringBoot整合mapstruct,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-11-11
教您如何3分鐘快速搞定EasyExcel導(dǎo)入與導(dǎo)出功能
對于EasyExcel庫,我們可以使用它來實(shí)現(xiàn)數(shù)據(jù)的導(dǎo)入和導(dǎo)出,下面這篇文章主要給大家介紹了關(guān)于如何3分鐘快速搞定EasyExcel導(dǎo)入與導(dǎo)出功能的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-01-01
java監(jiān)聽器的實(shí)現(xiàn)和原理詳解
這篇文章主要給大家介紹了關(guān)于java監(jiān)聽器實(shí)現(xiàn)和原理的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用java具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08
Spring Boot 如何使用Liquibase 進(jìn)行數(shù)據(jù)庫遷移(操作方法)
在Spring Boot應(yīng)用程序中使用Liquibase進(jìn)行數(shù)據(jù)庫遷移是一種強(qiáng)大的方式來管理數(shù)據(jù)庫模式的變化,本文重點(diǎn)講解如何在Spring Boot應(yīng)用程序中使用Liquibase進(jìn)行數(shù)據(jù)庫遷移,從而更好地管理數(shù)據(jù)庫模式的變化,感興趣的朋友跟隨小編一起看看吧2023-09-09
java調(diào)用webservice接口,并解析返回參數(shù)問題
這篇文章主要介紹了java調(diào)用webservice接口,并解析返回參數(shù)問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-07-07
Java面向?qū)ο笤O(shè)計(jì)原則之迪米特法則介紹
迪米特法則解決類與類之間耦合度問題,如果類A調(diào)用了B類的某一個(gè)方法,則這兩個(gè)類就形成了一種緊耦合的方式,當(dāng)B類這個(gè)方法發(fā)生變化時(shí),一定會影響A類的執(zhí)行結(jié)果。迪米特法則要求每一個(gè)類盡可能少的與其他類發(fā)生關(guān)系2023-02-02

