SpringBoot使用freemarker導(dǎo)出word文件方法詳解
1、前言
在項(xiàng)目中我們有時(shí)間需要根據(jù)一個(gè)word模板文檔,批量生成其他的word文檔,里面的有些值改變一下而已,那怎么做呢?
2、需求說明
假如說,現(xiàn)在我有個(gè)模板文檔,內(nèi)容如下:
現(xiàn)在上面文檔里面有如下變量:
- username:?jiǎn)T工姓名
- idno:身份證號(hào)碼
- hireDate:入職日期
- work:職位
- endDate:離職日期
現(xiàn)在我需要針對(duì)不同的員工一鍵生成一份離職證明出來,公司章是一張圖片,也需要生成進(jìn)去,下面我們開始測(cè)試
3、編碼
基礎(chǔ)框架代碼地址:https://gitee.com/colinWu_java/spring-boot-base.git
我會(huì)在此主干基礎(chǔ)上開發(fā)
3.1、導(dǎo)入依賴
<!--freemarker--> <dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.30</version> </dependency>
3.2、接口編寫
TestController新增下面接口:
/** * 根據(jù)ftl模板模板下載word文檔 */ @GetMapping("/downloadWord") public void downloadWord(HttpServletResponse response){ Map<String, Object> map = new HashMap<>(); map.put("username", "王天霸");//姓名 map.put("idno", "429004199601521245");//身份證號(hào) map.put("hireDate", "2021年12月12日");//入職日期 map.put("work", "Java軟件工程師");//職務(wù) map.put("endDate", "2022年11月25日");//離職日期 map.put("imageData", Tools.getBase64ByPath("D:\\demo\\test.png"));//蓋章Base64數(shù)據(jù)(不包含頭信息) try { FreemarkerExportWordUtil.exportWord(response, map, "離職證明.doc", "EmploymentSeparationCertificate.ftl"); } catch (IOException e) { e.printStackTrace(); } }
D:\demo\test.png這張圖片就是下面這張圖片:
3.3、工具類
Tools工具類:
package org.wujiangbo.utils; import org.wujiangbo.domain.user.User; import sun.misc.BASE64Encoder; import java.io.*; import java.util.ArrayList; import java.util.List; /** * <p>工具類</p> */ public class Tools { /** * 獲得指定圖片文件的base64編碼數(shù)據(jù) * @param filePath 文件路徑 * @return base64編碼數(shù)據(jù) */ public static String getBase64ByPath(String filePath) { if(!hasLength(filePath)){ return ""; } File file = new File(filePath); if(!file.exists()) { return ""; } InputStream in = null; byte[] data = null; try { in = new FileInputStream(file); } catch (FileNotFoundException e1) { e1.printStackTrace(); } try { assert in != null; data = new byte[in.available()]; in.read(data); in.close(); } catch (IOException e) { e.printStackTrace(); } BASE64Encoder encoder = new BASE64Encoder(); return encoder.encode(data); } /** * @desc 判斷字符串是否有長(zhǎng)度 */ public static boolean hasLength(String str) { return org.springframework.util.StringUtils.hasLength(str); } }
FreemarkerExportWordUtil工具類:
package org.wujiangbo.utils; import freemarker.template.Configuration; import freemarker.template.Template; import freemarker.template.Version; import javax.servlet.http.HttpServletResponse; import java.io.*; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.Map; /** * 模板word導(dǎo)出,工具類 */ public class FreemarkerExportWordUtil { private static Configuration configuration = null; static { configuration = new Configuration(new Version("2.3.0")); configuration.setDefaultEncoding("utf-8"); //獲取模板路徑 setClassForTemplateLoading 這個(gè)方法默認(rèn)路徑是webRoot 路徑下 configuration.setClassForTemplateLoading(FreemarkerExportWordUtil.class, "/templates"); } private FreemarkerExportWordUtil() { throw new AssertionError(); } /** * 根據(jù) /resources/templates 目錄下的ftl模板文件生成文件并寫到客戶端進(jìn)行下載 * @param response HttpServletResponse * @param map 數(shù)據(jù)集合 * @param fileName 用戶下載到的文件名稱 * @param ftlFileName ftl模板文件名稱 * @throws IOException */ public static void exportWord(HttpServletResponse response, Map map, String fileName, String ftlFileName) throws IOException { Template freemarkerTemplate = configuration.getTemplate(ftlFileName); // 調(diào)用工具類的createDoc方法生成Word文檔 File file = createDoc(map, freemarkerTemplate); //將word文檔寫到前端 download(file.getAbsolutePath(), response, fileName); } private static File createDoc(Map<?, ?> dataMap, Template template) { //臨時(shí)文件 String name = "template.doc"; File f = new File(name); Template t = template; try { // 這個(gè)地方不能使用FileWriter因?yàn)樾枰付ň幋a類型否則生成的Word文檔會(huì)因?yàn)橛袩o法識(shí)別的編碼而無法打開 Writer w = new OutputStreamWriter(new FileOutputStream(f), "utf-8"); t.process(dataMap, w); w.close(); } catch (Exception ex) { ex.printStackTrace(); throw new RuntimeException(ex); } return f; } //下載文件的公共方法 public static void download(String filePath, HttpServletResponse response, String fileName) { try { setAttachmentResponseHeader(response, fileName); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } try( BufferedInputStream bis = new BufferedInputStream(new FileInputStream(filePath)); // 輸出流 BufferedOutputStream bos = new BufferedOutputStream(response.getOutputStream()); ){ byte[] buff = new byte[1024]; int len = 0; while ((len = bis.read(buff)) > 0) { bos.write(buff, 0, len); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } /** * 下載文件名重新編碼 * @param response 響應(yīng)對(duì)象 * @param realFileName 真實(shí)文件名 * @return */ public static void setAttachmentResponseHeader(HttpServletResponse response, String realFileName) throws UnsupportedEncodingException { String percentEncodedFileName = percentEncode(realFileName); StringBuilder contentDispositionValue = new StringBuilder(); contentDispositionValue.append("attachment; filename=") .append(percentEncodedFileName) .append(";") .append("filename*=") .append("utf-8''") .append(percentEncodedFileName); response.addHeader("Access-Control-Allow-Origin", "*"); response.addHeader("Access-Control-Expose-Headers", "Content-Disposition,download-filename"); response.setHeader("Content-disposition", contentDispositionValue.toString()); response.setHeader("download-filename", percentEncodedFileName); } /** * 百分號(hào)編碼工具方法 * * @param s 需要百分號(hào)編碼的字符串 * @return 百分號(hào)編碼后的字符串 */ public static String percentEncode(String s) throws UnsupportedEncodingException { String encode = URLEncoder.encode(s, StandardCharsets.UTF_8.toString()); return encode.replaceAll("\\+", "%20"); } }
3.4、ftl文件
那么如何制作出ftl模板文件呢?很簡(jiǎn)單,跟著下面步驟做即可
1、首先新建一個(gè)demo.doc文檔,然后內(nèi)容像下面這樣寫好:
2、將該word文檔導(dǎo)出成demo.xml文檔,如下:
然后:
這樣桌面就會(huì)多一個(gè)demo.xml文件,此時(shí)關(guān)閉demo.doc文件,打開demo.xml文件進(jìn)行編輯
3、修改Demo.xml文件
在demo.xml文件中找到剛才所有的變量,然后都用 包 裹 一 下 , 如 : u s e r n a m e 改 成 {}包裹一下,如:username改成 包裹一下,如:username改成{username},idno改成${idno},等等,全部改完
最后搜索pkg:binaryData字符串,用這個(gè)包裹著很大一段字符串,那就是文檔中公章圖片的Base64格式數(shù)據(jù),我們將其全部刪除,用${imageData}變量代替即可,待會(huì)從代碼中給這個(gè)變量賦值即可,就可以將真正的公章圖片替換到文檔中了,而且圖片大小格式什么的,都會(huì)和模板文檔中保持一致,所以模板文檔的格式先調(diào)整后之后再另存為xml文件
修改完成之后,將文件的后綴名由xml改成ftl,然后拷貝到項(xiàng)目resources下的templates目錄下,我這里改成:EmploymentSeparationCertificate.ftl了
3.5、測(cè)試
打開瀏覽器,訪問接口:http://localhost:8001/downloadWord,就可以下載一個(gè)word文檔了,打開后內(nèi)容如下:
OK,到此測(cè)試成功了
4、word轉(zhuǎn)pdf
有些場(chǎng)景需要導(dǎo)出pdf,那么就需要將生成的臨時(shí)word文件轉(zhuǎn)成pdf后再導(dǎo)出了,下面就介紹一下word轉(zhuǎn)成pdf的方法吧
首先需要導(dǎo)入依賴:
<dependency> <groupId>com.documents4j</groupId> <artifactId>documents4j-local</artifactId> <version>1.0.3</version> </dependency> <dependency> <groupId>com.documents4j</groupId> <artifactId>documents4j-transformer-msoffice-word</artifactId> <version>1.0.3</version> </dependency>
工具類:
/** * word文檔轉(zhuǎn)成PDF文檔 * @param wordPath word文檔路徑 * @param pdfPath pdf文檔路徑 */ public static void word2pdf(String wordPath, String pdfPath){ File inputWord = new File(wordPath); File outputFile = new File(pdfPath); InputStream docxInputStream = null; OutputStream outputStream = null; IConverter converter = null; try { docxInputStream = new FileInputStream(inputWord); outputStream = new FileOutputStream(outputFile); converter = LocalConverter.builder().build(); converter.convert(docxInputStream).as(DocumentType.DOCX).to(outputStream).as(DocumentType.PDF).execute(); outputStream.close(); } catch (Exception e) { e.printStackTrace(); log.error("word文檔轉(zhuǎn)成PDF文檔時(shí),發(fā)生異常:{}", e.getLocalizedMessage()); } finally { //關(guān)閉資源 try { if(docxInputStream != null){ docxInputStream.close(); } if(outputStream != null){ outputStream.close(); } if(converter != null){ converter.shutDown(); } } catch (IOException e) { e.printStackTrace(); } } }
需要注意的是:要使用這個(gè)工具類的話,運(yùn)行這個(gè)代碼的機(jī)器上需要安裝WPS或者office軟件,否則轉(zhuǎn)換過程中會(huì)報(bào)錯(cuò)(這可能是一個(gè)小瑕疵,我正在尋找其他更加優(yōu)雅的轉(zhuǎn)換方案,有更好辦法的小伙伴,歡迎留言)
我們就調(diào)用這個(gè)工具類將剛才生成的word文檔轉(zhuǎn)成pdf,成功之后內(nèi)容如下:
內(nèi)容和word文檔一致,測(cè)試成功
5、總結(jié)
本文主要介紹了根據(jù)word模板如何導(dǎo)出word文件,已經(jīng)將word文檔如何轉(zhuǎn)成pdf文件大家趕緊練習(xí)起來吧
到此這篇關(guān)于SpringBoot使用freemarker導(dǎo)出word文件方法詳解的文章就介紹到這了,更多相關(guān)SpringBoot導(dǎo)出word內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
基于javaweb+jsp的游泳館會(huì)員管理系統(tǒng)(附源碼)
這篇文章主要介紹了基于javaweb+jsp的游泳館會(huì)員管理系統(tǒng),開發(fā)工具eclipse/idea/myeclipse/sts等均可配置運(yùn)行,此源代碼社和課程設(shè)計(jì),大作業(yè)及畢業(yè)設(shè)計(jì)項(xiàng)目,需要的朋友可以參考下2022-04-04Mybatis結(jié)果集映射一對(duì)多簡(jiǎn)單入門教程
本文給大家介紹Mybatis結(jié)果集映射一對(duì)多簡(jiǎn)單入門教程,包括搭建數(shù)據(jù)庫(kù)環(huán)境的過程,idea搭建maven項(xiàng)目的代碼詳解,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友參考下吧2021-06-06ZooKeeper入門教程二在單機(jī)和集群環(huán)境下的安裝搭建及使用
本文是ZooKeeper入門系列教程,涵蓋ZooKeeper的安裝使及單機(jī)集群環(huán)境搭建,通過實(shí)例和大量圖表,結(jié)合實(shí)戰(zhàn),幫助學(xué)習(xí)者理解和運(yùn)用,有需要的朋友可以借鑒參考下2022-01-01java操作mongodb基礎(chǔ)(查詢 排序 輸出list)
java操作mongodb基礎(chǔ)學(xué)習(xí)查詢,排序,limit,輸出為list實(shí)例,大家參考使用吧2013-12-12Java遞歸來實(shí)現(xiàn)漢諾塔游戲,注釋詳細(xì)
這篇文章介紹了Java遞歸來實(shí)現(xiàn)漢諾塔游戲的方法,文中的代碼注釋介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-11-11