freemarker實現單元格動態合併-行合併
專案需求:專案中有個需求,需要將一些資料庫中的資料根據需求匯出,生成一個word,研究了一些技術,其中包括POI、freemaker,對比了一下實現過程及技術難度沒最終使用了freemaker;
原始檔案
效果:
實現過程大概分為三步,第一步:根據word檔案做模板,修改模板,匯出word。這裡主要記錄一下過程中遇到的一些情況。
一、製作模板
開啟word檔案,為要新增資料的地方打標籤,,使用${key}的方式,key將來就是資料Map中的key,需要保持一致。然後另存為XML,因為word本質上就是個XML
二、處理XML檔案
使用FirstObject XML Edito或者其他工具開啟,我是用的是NotePad++,開啟需要格式話一下,要不然沒法看,開啟主要是處理以下的情況:
這個是分開的,需要將中間的部分刪除,處理後如下:
全部處理好之後,檔案另存為ftl檔案,這個就是我們製作完成的模板。
三、資料處理、打標籤
這裡主要說的是需要迴圈並且還需要合併單元格的情況。如果沒有這些情況,直接打標籤,封裝資料就可以了。
第一個:list
主要是將資料封裝到list中,list中是若干個Map,如圖:
注意:list一定要放在要迴圈行開始的位置,及前一行結束的位置:
list結束位置:
四、資料準備
資料封裝,資料放在Map中,這裡Map中的key就是${key}中的key:
public void exportWord(HttpServletRequest request, HttpServletResponse response){ Map<String, Object> dataMap = new HashMap<>(16); dataMap.put("total", "10"); List<Map<String,String>> list=new ArrayList<>( 16 ); for(int i=0;i<3;i++){ Map<String, String> listMap= new HashMap<>(16); listMap.put( "no","10000"+i ); listMap.put( "name","test"+i ); listMap.put( "introduce","介紹"+i ); list.add( listMap ); } dataMap.put( "whyc",checkList( list ) ); System.out.println(dataMap); WordUtil.exportMillCertificateWord(response,dataMap,"test.docx","test.ftl"); } public List<Map<String, String>> checkList(List<Map<String, String>> list) { String start = "<w:vMerge w:val='restart'/>"; String end = "<w:vMerge/>"; list.get(0).put("start", start); for (int i = 1; i < list.size(); i++) { list.get(i).put("end", end); } return list; }
原則一、第一行資料只放"<w:vMerge w:val='restart'/>",從第二行開始,所有要合併的單元格放"<w:vMerge/>"。比如,我這個total這一行要合併,所以要這麼處理:
public List<Map<String, String>> checkList(List<Map<String, String>> list) { String start = "<w:vMerge w:val='restart'/>"; String end = "<w:vMerge/>"; list.get(0).put("start", start); for (int i = 1; i < list.size(); i++) { list.get(i).put("end", end); } return list; }View Code
資料輸出是這樣的:
原則二、編輯ftl模板檔案,start和end的位置,一定放在行迴圈開始的<w:tcPr>中:
最後是檔案匯出及下載的程式碼:
package com.thupdi.project.utils; import freemarker.template.Configuration; import freemarker.template.Template; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletResponse; import java.io.*; import java.net.URLEncoder; import java.util.Map; /** * @Author wangshuaijun * @Date 2019/7/18 13:59 * @Version 1.0 */ public class WordUtil { private static Configuration configure; static { configure= new Configuration(); configure.setDefaultEncoding("utf-8"); // configure.setClassForTemplateLoading(WordUtil.class,"com/thupdi/project/templates"); try { configure.setDirectoryForTemplateLoading(new File( "D:/develop/IJWorkspaces/fzlsmc/src/main/resources/wordteplate" )); } catch (IOException e) { e.printStackTrace(); } } public WordUtil() { super(); } /** * 根據Doc模板生成word檔案 * @param fileName 檔名稱 * @throws IOException */ public static void exportMillCertificateWord(HttpServletResponse response, Map<String,Object> map, String fileName, String ftlFile){ Template freemarkerTemplate = null; try { freemarkerTemplate = configure.getTemplate(ftlFile); } catch (IOException e1) { e1.printStackTrace(); } File file = null; InputStream fin = null; ServletOutputStream out = null; try { // 呼叫工具類的createDoc方法生成Word文件 file = createDoc(map,freemarkerTemplate,fileName); fin = new FileInputStream(file); response.setCharacterEncoding("utf-8"); response.setContentType("application/msword"); // 設定瀏覽器以下載的方式處理該檔名 fileName = fileName + ".doc"; response.setHeader("Content-Disposition", "attachment;filename=" .concat(String.valueOf( URLEncoder.encode(fileName, "UTF-8")))); out = response.getOutputStream(); byte[] buffer = new byte[512]; // 緩衝區 int bytesToRead = -1; // 通過迴圈將讀入的Word檔案的內容輸出到瀏覽器中 while((bytesToRead = fin.read(buffer)) != -1) { out.write(buffer, 0, bytesToRead); } } catch (IOException e) { e.printStackTrace(); } finally { if (fin != null) { try { fin.close(); } catch (IOException e) { e.printStackTrace(); } } if (out != null) { try { out.close(); } catch (IOException e) { e.printStackTrace(); } } // 刪除臨時檔案 if (file != null) { file.delete(); } } } /** * 生成word * @param dataMap * @param template * @param fileName * @return */ private static File createDoc(Map<?, ?> dataMap, Template template,String fileName) { File f = new File(fileName); Template t = template; try { // 這個地方不能使用FileWriter因為需要指定編碼型別否則生成的Word文件會因為有無法識別的編碼而無法開啟 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; } }View Code
Maven依賴:
<!-- https://mvnrepository.com/artifact/org.freemarker/freemarker --> <dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.20</version> </dependency>View Code
&n