天天看點

springboot中使用freemarker生成word文檔并打包成zip下載下傳(履歷)

一、設計出的履歷模闆圖以及給的履歷小圖示切圖

springboot中使用freemarker生成word文檔并打包成zip下載下傳(履歷)
springboot中使用freemarker生成word文檔并打包成zip下載下傳(履歷)

二、按照履歷模闆圖建立履歷word檔案 :${字段名},同時将圖檔插入到word中,并将建好的word檔案另存為xml檔案;

springboot中使用freemarker生成word文檔并打包成zip下載下傳(履歷)
springboot中使用freemarker生成word文檔并打包成zip下載下傳(履歷)

三、直接将該xml檔案重命名為.ftl檔案,并用編輯器(EditPlus)打開并修改

說明:字段取值用Map來存取;

   ${pictureAddrees!"...."}    pictureAddress中存的是圖檔轉換後的64位碼,!(感歎号)表示當字段值為空時取後面的預設圖檔的64位碼;

      集合資料循環取值形式如圖所示。

springboot中使用freemarker生成word文檔并打包成zip下載下傳(履歷)
springboot中使用freemarker生成word文檔并打包成zip下載下傳(履歷)

四、項目pom檔案中加入freemarker的依賴,将ftl檔案放到resource目錄下

<!--添加freeMarker-->  
             <!-- https://mvnrepository.com/artifact/org.freemarker/freemarker -->
		<dependency>
		    <groupId>org.freemarker</groupId>
		    <artifactId>freemarker</artifactId>
		    <version>2.3.23</version>
		 </dependency>
      

  

springboot中使用freemarker生成word文檔并打包成zip下載下傳(履歷)

五、工具類代碼如下:

1、createWord(Map dataMap, String templateName, String fileFullPath)   ==>  根據傳入的資料、模闆檔案名、生成檔案全路徑名(帶.doc)來建立word檔案到磁盤;

2、createZip(String zipfullPath,String[] fileFullPaths)    ==>  用流的方式根據生成的檔案路徑名(帶.zip)、要打包的word檔案全路徑名數組(帶.doc)來打包zip檔案到磁盤;

3、createZip(String zipfullPath,String fileFullPath,boolean isKeepDirStr)  ==> 用流的方式生成zip檔案,調用compressZip()方法

   compressZip(InputStream inputStream,ZipOutputStream zip, File sourceFile, String fileName,boolean isKeepDirStr) ==> 遞歸壓縮檔案夾,被調用

  注意:當生成的zip檔案為帶檔案夾目錄級别時,調用3方法;

     當生成的zip檔案為純檔案時,調用2方法。

4、downLoadFile(String fullPath, HttpServletResponse response)  ==> 用流的方式下載下傳生成的word檔案、zip檔案或其他檔案;

5、createFromUrl(String urlAddress,String fileFullPath) ==> 從網絡位址下載下傳檔案到磁盤;

  如插入履歷的圖檔需要從網絡位址下載下傳到磁盤,再生成base64位碼,否則會失敗;

  個人的一些視訊資訊位址、附件位址也需要從網絡位址下載下傳到磁盤,儲存後再一起和履歷word打包成zip檔案下載下傳。

6、getImageBase(String urlAddress,String pathAddress) ==> 生成圖檔的Base64位碼。

springboot中使用freemarker生成word文檔并打包成zip下載下傳(履歷)
1 package com.hs.zp.common.utils;
  2 
  3 import freemarker.template.Configuration;
  4 import freemarker.template.Template;
  5 import freemarker.template.TemplateExceptionHandler;
  6 
  7 import java.io.*;
  8 import java.net.URL;
  9 import java.util.Map;
 10 
 11 import javax.servlet.http.HttpServletResponse;
 12 import javax.xml.soap.Text;
 13 
 14 import org.apache.commons.codec.binary.Base64;
 15 import org.apache.log4j.Logger;
 16 import org.apache.tools.zip.ZipEntry;
 17 import org.apache.tools.zip.ZipOutputStream;
 18 
 19 import com.google.common.io.Files;
 20 
 21 /**
 22  * 
 23  * @Descript TODO (利用freemark生成word及zip)
 24  * @author yeting
 25  * @date 2019年3月19日
 26  *
 27  */
 28 public class WordUtil {
 29     public static Logger logger = Logger.getLogger(WordUtil.class);
 30      
 31     /**
 32      * 生成word檔案(全局可用)
 33      * @param dataMap word中需要展示的動态資料,用map集合來儲存
 34      * @param templateName word模闆名稱,例如:test.ftl
 35      * @param fileFullPath 要生成的檔案全路徑
 36      */
 37     @SuppressWarnings("unchecked")
 38     public static void createWord(Map dataMap, String templateName, String fileFullPath) {
 39         logger.info("【createWord】:==>方法進入");
 40         logger.info("【fileFullPath】:==>" + fileFullPath);
 41         logger.info("【templateName】:==>" + templateName);
 42         
 43         try {
 44             // 建立配置執行個體
 45             Configuration configuration = new Configuration();
 46             logger.info("【建立配置執行個體】:==>");
 47             
 48             // 設定編碼
 49             configuration.setDefaultEncoding("UTF-8");
 50             logger.info("【設定編碼】:==>");
 51             
 52             // 設定處理空值
 53             configuration.setClassicCompatible(true);
 54             
 55             // 設定錯誤控制器
 56 //            configuration.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
 57             
 58 //            String pathName = Text.class.getResource("/template").getFile();
 59 //            File templateFile = new File(pathName);
 60 //            logger.info("【pathName】:==>" + pathName);
 61 //            logger.info("【templateFile】:==>" + templateFile.getName());
 62 //            configuration.setDirectoryForTemplateLoading(templateFile);
 63             
 64             // 設定ftl模闆檔案加載方式
 65             configuration.setClassForTemplateLoading(WordUtil.class,"/template");
 66             
 67             //建立檔案
 68             File file = new File(fileFullPath);
 69             // 如果輸出目标檔案夾不存在,則建立
 70             if (!file.getParentFile().exists()) {
 71                 file.getParentFile().mkdirs();
 72             }
 73 
 74             // 将模闆和資料模型合并生成檔案
 75             Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "UTF-8"));
 76             // 擷取模闆
 77             Template template = configuration.getTemplate(templateName);
 78             // 生成檔案
 79             template.process(dataMap, out);
 80 
 81             // 清空緩存
 82             out.flush();
 83             // 關閉流
 84             out.close();
 85 
 86         } catch (Exception e) {
 87             logger.info("【生成word檔案出錯】:==>" + e.getMessage());
 88             e.printStackTrace();
 89         }
 90     }
 91     
 92     /**
 93      * 生成zip檔案,根據檔案路徑不帶子檔案夾(全局可用)
 94      * @param zipfullPath 壓縮後的zip檔案全路徑
 95      * @param fileFullPaths 壓縮前的檔案全路徑數組
 96      */
 97     public static void createZip(String zipfullPath,String[] fileFullPaths) {
 98         InputStream inputStream = null;
 99         ZipOutputStream zip = null;
100 
101         try {
102             zip = new ZipOutputStream(new FileOutputStream(zipfullPath));
103             zip.setEncoding("gbk");
104             
105             for(String fullPath:fileFullPaths) {
106                 logger.info("【createZip:fullPath】:==>" + fullPath);
107                 
108                 if(StringUtil.isNullOrEmpty(fullPath)) {
109                     continue;
110                 }
111                 
112                 //建立檔案
113                 File file = new File(fullPath);
114                 String fileName = file.getName();
115                 
116                 //讀檔案流
117                 inputStream = new BufferedInputStream(new FileInputStream(file));
118                 byte[] buffer = new byte[inputStream.available()];
119                 inputStream.read(buffer);
120                 inputStream.close();
121                 
122                 //将讀取的檔案輸出到zip中
123                 zip.putNextEntry(new ZipEntry(fileName)); 
124                 zip.write(buffer);
125                 zip.closeEntry(); 
126             }
127             
128         } catch (Exception e) {
129             e.printStackTrace();
130         } finally {
131             try {
132                 if (inputStream != null) {
133                     inputStream.close();
134                 }
135             } catch (Exception e) {
136                 e.printStackTrace();
137             }
138             
139             try {
140                 if (zip != null) {
141                     zip.close();
142                 }
143             } catch (Exception e) {
144                 e.printStackTrace();
145             }
146 
147         }
148     }
149     
150     /**
151      * 生成的zip檔案帶子檔案夾(全局可用)
152      * @param zipfullPath 壓縮後的zip檔案全路徑
153      * @param fileFullPath 壓縮前的檔案全路徑
154      * @param isKeepDirStr 是否保留原來的目錄結構,true:保留目錄結構; false:所有檔案跑到壓縮包根目錄下(注意:不保留目錄結構可能會出現同名檔案,會壓縮失敗)
155      */
156     public static void createZip(String zipfullPath,String fileFullPath,boolean isKeepDirStr) {
157         InputStream inputStream = null;
158         ZipOutputStream zip = null;
159 
160         try {
161             zip = new ZipOutputStream(new FileOutputStream(zipfullPath));
162             zip.setEncoding("gbk");
163             
164             File file = new File(fileFullPath);
165             
166             compressZip(inputStream,zip,file, file.getName(), isKeepDirStr);//遞歸壓縮
167             
168         } catch (Exception e) {
169             e.printStackTrace();
170         } finally {
171             try {
172                 if (inputStream != null) {
173                     inputStream.close();
174                 }
175             } catch (Exception e) {
176                 e.printStackTrace();
177             }
178             
179             try {
180                 if (zip != null) {
181                     zip.close();
182                 }
183             } catch (Exception e) {
184                 e.printStackTrace();
185             }
186 
187         }
188     }
189     
190     /**
191      * 遞歸壓縮方法(僅限于此類中用于壓縮zip檔案)
192      * @param inputStream 輸入流
193      * @param zip zip輸出流
194      * @param sourceFile 源檔案
195      * @param fileName 檔案夾名或檔案名
196      * @param isKeepDirStr 是否保留原來的目錄結構,true:保留目錄結構; false:所有檔案跑到壓縮包根目錄下(注意:不保留目錄結構可能會出現同名檔案,會壓縮失敗)
197      * @throws Exception
198      */
199     private static void compressZip(InputStream inputStream,ZipOutputStream zip, File sourceFile, String fileName,boolean isKeepDirStr) throws Exception{
200         logger.info("【compressZip:sourceFile】:==>" + sourceFile.getPath());
201         logger.info("【compressZip:fileName】:==>" + fileName);
202         
203         if(sourceFile.isFile()){
204             //讀檔案流
205             inputStream = new BufferedInputStream(new FileInputStream(sourceFile));
206             byte[] buffer = new byte[inputStream.available()];
207             inputStream.read(buffer);
208             inputStream.close();
209             
210             //将讀取的檔案輸出到zip中
211             zip.putNextEntry(new ZipEntry(fileName));
212             zip.write(buffer);
213             zip.closeEntry(); 
214         } else {
215             File[] listFiles = sourceFile.listFiles();
216             if(listFiles == null || listFiles.length == 0){
217                 // 需要保留原來的檔案結構時,需要對空檔案夾進行處理
218                 if(isKeepDirStr){
219                     zip.putNextEntry(new ZipEntry(fileName + "/"));//空檔案夾的處理
220                     zip.closeEntry();// 沒有檔案,不需要檔案的copy
221                 }
222             }else {
223                 for (File file : listFiles) {
224                     // 判斷是否需要保留原來的檔案結構,注意:file.getName()前面需要帶上父檔案夾的名字加一斜杠,不然最後壓縮包中就不能保留原來的檔案結構,即:所有檔案都跑到壓縮包根目錄下了
225                     if (isKeepDirStr) { 
226                         compressZip(inputStream,zip,file,  fileName + "/" + file.getName(),isKeepDirStr);
227                     } else {
228                         compressZip(inputStream,zip, file, file.getName(),isKeepDirStr);
229                     }
230                 }
231             }
232         }
233     }
234     
235     /**
236      * 下載下傳生成的檔案(全局可用)
237      * @param fullPath 全路徑
238      * @param response
239      */
240     public static void downLoadFile(String fullPath, HttpServletResponse response) {
241         logger.info("【downLoadFile:fullPath】:==>" + fullPath);
242         
243         InputStream inputStream = null;
244         OutputStream outputStream = null;
245 
246         try {
247             //建立檔案
248             File file = new File(fullPath);
249             String fileName = file.getName();
250             
251             //讀檔案流
252             inputStream = new BufferedInputStream(new FileInputStream(file));
253             byte[] buffer = new byte[inputStream.available()];
254             inputStream.read(buffer);
255             
256             //清空響應
257             response.reset();
258             response.setCharacterEncoding("UTF-8");
259             response.setContentType("application/octet-stream; charset=utf-8");
260             // response.setContentType("application/msword");
261             response.setHeader("Content-Disposition","attachment; filename=" + new String(fileName.getBytes(), "ISO8859-1"));
262             response.setHeader("Content-Length", "" + file.length());
263             
264             //寫檔案流
265             outputStream = new BufferedOutputStream(response.getOutputStream());
266             outputStream.write(buffer);
267             outputStream.flush();
268         } catch (Exception e) {
269             e.printStackTrace();
270         } finally {
271             try {
272                 if (outputStream != null) {
273                     outputStream.close();
274                 }
275             } catch (Exception e) {
276                 e.printStackTrace();
277             }
278             try {
279                 if (inputStream != null) {
280                     inputStream.close();
281                 }
282             } catch (Exception e) {
283                 e.printStackTrace();
284             }
285 
286         }
287     }
288     
289     /**
290      * 下載下傳網絡檔案到本地(主要用于下載下傳履歷附件)
291      * @param urlAddress 網絡url位址,為空時直接傳回
292      * @param fileFullPath 檔案全路徑
293      */
294     public static void createFromUrl(String urlAddress,String fileFullPath) {
295         logger.info("【service:開始下載下傳網絡檔案】:==> 網上檔案位址:" + urlAddress + "檔案儲存路徑:" + fileFullPath);
296         
297         if(StringUtil.isNullOrEmpty(urlAddress)) {
298             return ;
299         }
300         
301         DataInputStream dataInputStream = null;
302         FileOutputStream fileOutputStream =null;
303         try {
304             
305             URL url = new URL(urlAddress);
306             
307             dataInputStream = new DataInputStream(url.openStream());//打開網絡輸入流
308 
309             //建立檔案
310             File file = new File(fileFullPath);
311             // 如果輸出目标檔案夾不存在,則建立
312             if (!file.getParentFile().exists()) {
313                 file.getParentFile().mkdirs();
314             }
315             
316             fileOutputStream = new FileOutputStream(file);//建立一個新的檔案
317             
318             byte[] buffer = new byte[1024];
319             int length;
320             
321             while((length = dataInputStream.read(buffer))>0){//開始填充資料
322                 fileOutputStream.write(buffer,0,length);
323             }
324             
325             fileOutputStream.flush();        
326         } catch (Exception e) {
327             e.printStackTrace();
328         } finally {
329             try {
330                 if(dataInputStream!=null) {
331                     dataInputStream.close();
332                 }
333             } catch (IOException e) {
334                 e.printStackTrace();
335             }
336             
337             try {
338                 if(fileOutputStream!=null) {
339                     fileOutputStream.close();
340                 }
341             } catch (IOException e) {
342                 e.printStackTrace();
343             }
344         }
345     }
346 
347     /**
348      * 從網上或本地獲得圖檔的base64碼(主要用于插入生成word中的圖檔)
349      * @param urlAddress 網絡路徑,二選一,目前有問題
350      * @param pathAddress 本地路徑,二選一
351      * @return 傳回base64碼或null
352      */
353     public static String getImageBase(String urlAddress,String pathAddress) {        
354         byte[] buffer = null;
355         InputStream inputStream = null;
356         String imageCodeBase64 = null;
357         
358         try {
359             if(!StringUtil.isNullOrEmpty(urlAddress)){
360                 URL url = new URL(urlAddress);
361                 inputStream = new DataInputStream(url.openStream());//打開網絡輸入流
362                 buffer = new byte[inputStream.available()];
363                 inputStream.read(buffer);
364             }else if(!StringUtil.isNullOrEmpty(pathAddress)){
365                 inputStream = new BufferedInputStream(new FileInputStream(new File(pathAddress)));//讀檔案流
366                 buffer = new byte[inputStream.available()];
367                 inputStream.read(buffer);
368             }else {
369                 return null;
370             }
371             
372             imageCodeBase64 = Base64.encodeBase64String(buffer);
373 //            System.out.println(imageCodeBase64);
374         }catch (Exception e) {
375             e.printStackTrace();
376         }finally {
377             try {
378                 if(inputStream!=null) {
379                     inputStream.close();
380                 }
381             } catch (IOException e) {
382                 e.printStackTrace();
383             }
384         }
385         return imageCodeBase64;
386     }
387     
388     
389 
390 
391 }      

六、調用處代碼如下

 邏輯:循環開始 ==>  

      取出履歷資料封裝到Map中  ==>  生成word檔案到磁盤 ==>  下載下傳附件等到磁盤 ==> 将word檔案、下載下傳好的檔案 的全路徑名放入到路徑數組中

      ==>  循環中....

    循環結束 ==>

    根據路徑數組打包生成zip到磁盤 ==>

    下載下傳zip檔案 ==>

    删除原檔案和zip檔案,下載下傳完畢 ==>

springboot中使用freemarker生成word文檔并打包成zip下載下傳(履歷)
springboot中使用freemarker生成word文檔并打包成zip下載下傳(履歷)
  
  1     @Transactional(rollbackFor=Exception.class)
  2     @Override
  3     public void exportWordResume(List<ResumeDelivery> resumeDeliveryList,int userId, HttpServletResponse response) throws Exception {    
  4         logger.info("【service:exportWordResume】:==> 服務層請求開始");
  5         
  6         String[] fileFullPaths = new String[resumeDeliveryList.size()];//檔案全路徑數組
  7         String[] folderFullPaths = new String[resumeDeliveryList.size()];//檔案夾全路徑數組
  8         String[] addUrls = new String[resumeDeliveryList.size()];//附件全路徑數組
  9         String[] videoFullPaths = new String[resumeDeliveryList.size()];//視訊全路徑數組
 10         
 11         boolean flag = false;//該批檔案是否存在附件
 12         String templateName = "jlmb.ftl";//模闆名稱
 13         Resume resume = null;//履歷
 14         Map<String, Object> map = null;//擷取資料資訊
 15         String fileName = null;//檔案名稱:應聘者姓名+應聘職位名稱+履歷唯一辨別号+下載下傳人ID
 16         String zipFullPath = filePath + File.separator + "小馬HR_求職者履歷下載下傳_" +userId;////要壓縮的檔案夾路徑
 17         String folderFullPath = null;//子檔案夾全路徑
 18         String fileFullPath = null;//檔案全路徑
 19         String addFullPath = null;//附件全路徑
 20         String addSuffix = null;//附件字尾
 21         String videoFullPath = null;//視訊全路徑
 22         String videoSuffix = null;//視訊字尾
 23         String headImagePath = null;//頭像全路徑
 24         
 25         String validString = null;
 26         Map<Integer,String> validMap = new HashMap<>();//履歷字串 key:resumeId,value:resumeId+positionName
 27         
 28         int index = 1;//履歷下載下傳數
 29         int count = 0;//循環次數
 30         for(ResumeDelivery resumeDeliveryBean:resumeDeliveryList) {
 31             count++;
 32             
 33             logger.info("【service:循環投遞記錄】:==> " + count);
 34             
 35             //判斷是否重複的履歷不予下載下傳
 36             validString = resumeDeliveryBean.getResumeId() + resumeDeliveryBean.getPositionName();
 37             if(validString.equals(validMap.get(resumeDeliveryBean.getResumeId()))){
 38                 logger.info("【重複履歷】:==> " + validString);
 39                 continue;
 40             }else {
 41                 validMap.put(resumeDeliveryBean.getResumeId(), validString);
 42             }
 43             
 44             Assert.notNull(resumeDeliveryBean.getResumeId(), "第" + count +"份履歷投遞記錄不存在!投遞ID:" + resumeDeliveryBean.getId());
 45             resume =  resumeMapper.selectByPrimaryKey(resumeDeliveryBean.getResumeId());
 46             Assert.notNull(resume, "第" + count +"份履歷不存在!投遞ID:" + resumeDeliveryBean.getId());
 47             
 48             //隐藏手機号
 49             if(resumeDeliveryBean.getStatus() != null
 50                     && (resumeDeliveryBean.getStatus() == 0 
 51                     || resumeDeliveryBean.getStatus() == 2 
 52                     || resumeDeliveryBean.getStatus() == 3)) {    //    已投遞、已過期、已淘汰 ==>隐藏手機号
 53                 if(!StringUtil.isNullOrEmpty(resumeDeliveryBean.getMobile())) {
 54                     resume.setMobile(resume.getMobile().substring(0, 3) + "****" + resume.getMobile().substring(resume.getMobile().length() - 4));
 55                 }
 56             }else if(resumeDeliveryBean.getEmployStatus() != null 
 57                     && (resumeDeliveryBean.getEmployStatus() == 2 
 58                     || resumeDeliveryBean.getEmployStatus() == 4 
 59                     || resumeDeliveryBean.getEmployStatus() == 10)) {            //    不合适、申訴中、已終止
 60                 if(!StringUtil.isNullOrEmpty(resumeDeliveryBean.getMobile())) {
 61                     resume.setMobile(resume.getMobile().substring(0, 3) + "****" + resume.getMobile().substring(resume.getMobile().length() - 4));
 62                 }
 63             }
 64             
 65             fileName = resume.getHunterName() + "_" + resumeDeliveryBean.getPositionName() + "_" + resume.getId()+"_" + userId;
 66             folderFullPath = zipFullPath + File.separator + fileName;
 67             fileFullPath = folderFullPath + File.separator + fileName + ".doc";
 68             addSuffix = StringUtil.isNullOrEmpty(resume.getEnclosureAddress()) ? "" : resume.getEnclosureAddress().substring(resume.getEnclosureAddress().lastIndexOf("."));
 69             addFullPath = folderFullPath + File.separator + fileName + "_附件"+ addSuffix;
 70             videoSuffix = StringUtil.isNullOrEmpty(resume.getVideoAddress()) ? "" : resume.getVideoAddress().substring(resume.getVideoAddress().lastIndexOf("."));
 71             videoFullPath = folderFullPath + File.separator + fileName + "_個人視訊"+ videoSuffix;
 72             headImagePath = folderFullPath + File.separator + fileName + "_頭像.jpg";
 73             
 74             WordUtil.createFromUrl(resume.getPictureAddress(), headImagePath);//先下載下傳頭像到本地,再插入到word中
 75             map = this.getResumeData(resume,headImagePath);
 76             
 77             logger.info("【service:開始生成word檔案】:==> 檔案名:" + fileFullPath);
 78             WordUtil.createWord(map, templateName, fileFullPath);//生成word檔案
 79             logger.info("【service:生成word檔案 完畢】:==>");
 80             
 81             FileUtil.deleteFile(headImagePath);//删除頭像圖檔
 82             
 83             WordUtil.createFromUrl(resume.getEnclosureAddress(), addFullPath);//下載下傳附件
 84             WordUtil.createFromUrl(resume.getVideoAddress(), videoFullPath);//下載下傳視訊
 85             
 86             
 87             //指派
 88             fileFullPaths[index - 1] = fileFullPath;
 89             folderFullPaths[index - 1] = folderFullPath;
 90             if(!StringUtil.isNullOrEmpty(addSuffix)) {
 91                 addUrls[index - 1] = addFullPath;
 92                 flag = true;
 93             }
 94             if(!StringUtil.isNullOrEmpty(videoSuffix)) {
 95                 videoFullPaths[index - 1] = videoFullPath;
 96                 flag = true;
 97             }
 98             
 99             index++;
100             if(index == 20) {//設定最多一次下載下傳10份履歷
101                 break;
102             }
103         }
104         
105         if(!flag) {
106             if(resumeDeliveryList.size()==1) {
107                 logger.info("【打包下載下傳一】:==>");
108                 
109                 WordUtil.downLoadFile(fileFullPaths[0], response);//下載下傳單個word檔案
110                 FileUtil.deleteFile(fileFullPaths[0]);
111             }else {        
112                 logger.info("【打包下載下傳二】:==>");
113                 
114                 String zipFileFullPath = zipFullPath + ".zip";//壓縮後的檔案名
115                 
116                 WordUtil.createZip(zipFileFullPath, fileFullPaths);//生成zip不帶附件不帶子檔案夾
117                 WordUtil.downLoadFile(zipFileFullPath, response);//下載下傳zip檔案
118                     
119                 FileUtil.deleteFile(zipFileFullPath);
120             }
121         }else {
122             if(resumeDeliveryList.size()==1) {
123                 logger.info("【打包下載下傳三】:==>");
124                 
125                 String zipFileFullPath = folderFullPaths[0] + ".zip";//壓縮後的檔案名
126                 String[] newfileFullPaths = new String[]{fileFullPaths[0],addUrls[0],videoFullPaths[0]};//需要下載下傳的檔案
127                 
128                 WordUtil.createZip(zipFileFullPath, newfileFullPaths);//生成zip帶附件不帶子檔案夾
129                 WordUtil.downLoadFile(zipFileFullPath, response);//下載下傳zip檔案
130             }else {
131                 logger.info("【打包下載下傳四】:==>");
132                 
133                 String zipFileFullPath = zipFullPath + ".zip";;//壓縮後的檔案名
134                 
135                 WordUtil.createZip(zipFileFullPath, zipFullPath , true);//生成zip帶附件
136                 WordUtil.downLoadFile(zipFileFullPath, response);//下載下傳zip檔案
137                 
138                 FileUtil.deleteFile(zipFileFullPath);
139             }
140         }
141         
142         FileUtil.deleteFileDir(zipFullPath);
143     }
144     
145     /**
146      * 履歷資訊轉Map
147      * @param resume 履歷對象
148      * @param headImagePath 頭像全路徑
149      * @return 傳回map集合
150      * @throws Exception 出生日期轉年齡可能會抛出異常
151      */
152     public Map<String, Object> getResumeData(Resume resume,String headImagePath) throws Exception{        
153         Map<String, Object> map = new HashMap<>();
154         map.put(Resume.EXPORT_HUNTER_NAME, resume.getHunterName());
155         map.put(Resume.EXPORT_SEX_STR, resume.getSexStr());
156         map.put(Resume.EXPORT_AGE, AgeUtil.getAgeByBirth(resume.getDateOfBirth()) + "歲");
157         map.put(Resume.EXPORT_WORHING_LENGTH_STR, resume.getWorkingLengthStr()==null ? "" : resume.getWorkingLengthStr());
158         map.put(Resume.EXPORT_MOBILE, resume.getMobile());
159         map.put(Resume.EXPORT_CREDENTIALS, resume.getCredentials());
160         map.put(Resume.EXPORT_INTRODUCE, resume.getIntroduce());
161         map.put(Resume.EXPORT_PICTURE_ADDRESS, StringUtil.isNullOrEmpty(resume.getPictureAddress()) ? null : WordUtil.getImageBase(null, headImagePath));//頭像
162         
163         map.put(Resume.EXPORT_INTENTION_POSITION, resume.getIntentionPosition());
164         map.put(Resume.EXPORT_INTENTION_LOCALE_STR, resume.getIntentionLocaleStr());
165         map.put(Resume.EXPORT_SALARY_UNIT_STR, resume.getSalaryExpectation());
166         map.put(Resume.EXPORT_RESUME_STATUS_STR, resume.getResumeStatusStr());
167         
168         map.put(Resume.EXPORT_STUDY_BEGIN_END_DATE, null);
169         map.put(Resume.EXPORT_GRADUATE_SCHOOL, resume.getGraduateSchool());
170         map.put(Resume.EXPORT_PROFESSION, resume.getProfession());
171         map.put(Resume.EXPORT_EDUCATIONAL_BACKGROUND_STR, resume.getEducationalBackgroundStr());
172         
173         map.put(Resume.EXPORT_HONORS, resume.getHonors());
174 
175         if(resume.getResumeExperience()!=null && resume.getResumeExperience().size()>0) {
176             List<Map<String,Object>> list = new ArrayList<>();
177             Map<String, Object> exprMap = null;
178             for(ResumeExperience re:resume.getResumeExperience()) {
179                 exprMap = new HashMap<>();
180                 exprMap.put(ResumeExperience.EXPORT_COMPANY_NAME, re.getCompanyName());
181                 exprMap.put(ResumeExperience.EXPORT_POSITION, re.getPosition());
182                 exprMap.put(ResumeExperience.EXPORT_WORD_DESC, re.getWorkDesc());
183                 exprMap.put(ResumeExperience.EXPORT_WORK_BEGIN_END_DATE, (re.getStartDate().replace("-", "/") + "-" + re.getEndDate().replace("-", "/")));
184                 list.add(exprMap);
185             }
186             
187             map.put(Resume.EXPORT_EXPERIENCE, list);
188         }
189 
190         return map;
191     }      

七、從測試環境下載下傳後的履歷如下

springboot中使用freemarker生成word文檔并打包成zip下載下傳(履歷)
springboot中使用freemarker生成word文檔并打包成zip下載下傳(履歷)

八、過程中出現的問題:

1、模闆檔案路徑找不到  ==> 相對路徑問題,檢查後解決;

2、空值字段報錯或顯示錯誤 ==> 工具類代碼中已解決;或修改.ftl檔案中,字段接受時設定預設值;

3、多個工作經曆隻顯示一個 ==> 資料傳值有誤,檢查後解決;

4、頭像不顯示 ==> 生成的圖檔的base64位碼有誤,工具類代碼中已解決;

5、doc檔案不生成 ==> 模闆檔案字段值有問題,檢查後解決;

6、下載下傳速度問題 ==> 目前限制隻能一次下載下傳20個。