vue如何導(dǎo)出文件流獲取附件名稱并下載(在response.headers里解析filename導(dǎo)出)
需求
之前實現(xiàn)的導(dǎo)出都是各自的業(yè)務(wù)層,調(diào)用接口,使用blob對象轉(zhuǎn)換,最終a標(biāo)簽導(dǎo)出,需要自定義文件名跟文件后綴。
現(xiàn)在統(tǒng)一在攔截器配置,根據(jù)后端返回的response.headers解析是否是文件流,統(tǒng)一做配置處理,然后對后端返回的filename進(jìn)行轉(zhuǎn)碼,后端統(tǒng)一配置文件名及類型。
前端只管a標(biāo)簽下載即可。
以往實現(xiàn)的方法(各自的業(yè)務(wù)層寫方法)
//數(shù)據(jù)導(dǎo)出 indexExport() { let statYear = { statDate: this.form.statDate, dataType: "1", }; let infoMsg = this.$notify.info({ title: "消息", message: "正在下載文件,勿退出,請稍后", duration: 0, }); gljyjcDataExport(statYear).then((res) => { infoMsg.close(); //下載成功,等待下載提示框關(guān)閉 this.$notify({ title: "成功", message: "下載完成", type: "success", }); let blob = new Blob([res], { type: "", }); let url = window.URL.createObjectURL(blob); const link = document.createElement("a"); // 創(chuàng)建a標(biāo)簽 link.href = url; link.download = "數(shù)據(jù)清單(" + this.form.statDate + ").xlsx"; // 重命名文件 link.click(); URL.revokeObjectURL(url); // 釋放內(nèi)存 }); },
現(xiàn)在實現(xiàn)的方法(axios里攔截器統(tǒng)一配置處理)
主要看注釋行“文件下載”,因為后端返回流文件時候攜帶的response.headers會多Content-Disposition這個字段。
然后拿到里邊的filename后,對filename包含的信息進(jìn)行轉(zhuǎn)碼就可
decodeURIComponent、decodeURI都可進(jìn)行轉(zhuǎn)碼,具體二者有啥區(qū)別,水平有限沒大看懂,可自行百度查閱符合選項
//攔截器里肯定有請求攔截代碼axios.interceptors.request。怕展示代碼冗余就不多貼了 ... axios.interceptors.response.use( response => { const res = response.data; const config = response.config; console.log(response.headers,"response.headers")//這塊可以看一下response.headers究竟是什么 // 文件下載(主要看這塊) if (response.headers['content-disposition']) { let downLoadMark = response.headers['content-disposition'].split(';'); if (downLoadMark[0] === 'attachment') { // 執(zhí)行下載 let fileName = downLoadMark[1].split('filename=')[1]; if (fileName) { //fileName = decodeURIComponent(filename);//對filename進(jìn)行轉(zhuǎn)碼 fileName = decodeURI(fileName); if (window.navigator.msSaveOrOpenBlob) { navigator.msSaveBlob(new Blob([res]), fileName); } else { let url = window.URL.createObjectURL(new Blob([res])); let link = document.createElement('a'); link.style.display = 'none'; link.href = url; link.setAttribute('download', fileName); document.body.appendChild(link); link.click(); return; } } else { return res; } } } // 全局異常處理(獲取code做正常的攔截操作,根據(jù)自己的業(yè)務(wù)層code寫符合的就可) if (res.code !== CODE_SUCCESS) { if (res.code == '205') { Message.error({ message: res.data || "登錄失敗" }); store.dispatch("user/logout").then(() => { window.location.reload(); }); return } if (res.code === WARN_TIP) { Message.warning({ message: res.message }); } if (res.code === LOGIN_FAIL) { Message.error({ message: res.message || "登錄失敗" }); } // 其他狀態(tài)碼特殊處理 return Promise.reject(new Error(res.message || "Error")); } return res; }, error => { // 防重復(fù)提交 if (error.message) { allowRequest(reqList, error.message.url); } if (error.response) { if (error.response.data.code == 600 && !tipCode) { tipCode = true; Message.error({ message: '系統(tǒng)登錄身份令牌失效,請重新登錄!' }); } else if (error.response.status == 500) { Message.error({ message: '系統(tǒng)異常' }); } } return Promise.reject(error); } );
以上是未解析之前瀏覽器看到的文件夾名
經(jīng)過decodeURIComponent或decodeURI解析后,前端就能獲取到后端返回的中文文件名了。
把文章鏈接復(fù)制粘貼給后端,讓大佬自己賞閱。
截止目前,前端能干的活就到此為止了。
那么有人就想問了,那后端response.headers里沒返回我想要的Content-Disposition,前端怎么捕獲。
對此呢,我又找我們后端大佬要了一下后端實現(xiàn)的代碼,我就原封不動貼出來了,因為我根本看不懂說的是什么意思
- Controller端代碼(啥是Controller,根本不懂)
@PostMapping(value="/exportAddresses") public Result exportAddresses(HttpServletResponse response){ String[] titles = new String[] {"id","tableCode","columnName"}; List<Map<String,Object>> objList = new ArrayList<>(); DownLoadFileController addressService; List<NpColumns> npColumnsList = npColumnsService.findByTableCode("APP_TASK_CASE_INFO"); for(NpColumns item : npColumnsList){ Map<String,Object> tempMap = new HashMap<>(); tempMap.put("id", item.getId()); tempMap.put("tableCode", item.getTableCode()); tempMap.put("columnName", item.getColumnName()); objList.add(tempMap); } try { FileUtils.exportExcel(response,"地址樹",titles,objList); return ResultGenerator.genSuccessResult("導(dǎo)出成功!"); }catch (Exception e){ e.printStackTrace(); return ResultGenerator.genFailResult("導(dǎo)出失敗!"); } }
- 工具類方法(啥是工具類,也不懂)
public static void exportExcel(HttpServletResponse response,String fileName,String[] titles,List<Map<String,Object>> result){ HSSFWorkbook wb; OutputStream output = null; String tempName = fileName; try { Date date = new Date(); SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd"); fileName +="_"+df.format(date)+".xls"; String encodedFilename = URLEncoder.encode(fileName, "UTF-8"); wb= new HSSFWorkbook(); HSSFSheet sh = wb.createSheet(); // 設(shè)置列寬 for(int i = 0; i < titles.length-1; i++){ sh.setColumnWidth( i, 256*15+184); } // 第一行表頭標(biāo)題,CellRangeAddress 參數(shù):行 ,行, 列,列 HSSFRow row = sh.createRow(0); HSSFCell cell = row.createCell(0); cell.setCellValue(new HSSFRichTextString(tempName)); //cell.setCellStyle(fontStyle(wb)); sh.addMergedRegion(new CellRangeAddress(0, 0, 0,titles.length-1)); // 第二行 HSSFRow row3 = sh.createRow(1); // 第二行的列 for(int i=0; i < titles.length; i++){ cell = row3.createCell(i); cell.setCellValue(new HSSFRichTextString(titles[i])); //cell.setCellStyle(fontStyle(wb)); } //填充數(shù)據(jù)的內(nèi)容 i表示行,z表示數(shù)據(jù)庫某表的數(shù)據(jù)大小,這里使用它作為遍歷條件 int i = 2, z = 0; while (z < result.size()) { row = sh.createRow(i); Map<String,Object> map = result.get(z); for(int j=0;j < titles.length;j++) { cell = row.createCell(j); if(map.get(titles[j]) !=null) { cell.setCellValue(map.get(titles[j]).toString()); }else { cell.setCellValue(""); } } i++; z++; } output = response.getOutputStream(); response.reset(); response.addHeader("Content-Type","application/octet-stream;charset=utf-8"); response.setHeader("Content-disposition", "attachment; filename="+encodedFilename); response.setContentType("application/msexcel"); wb.write(output); output.flush(); output.close(); }catch (Exception e){ e.printStackTrace(); } }
- 最后還有個中文處理亂碼那塊(這都是啥啥啥,還是不懂)
String encodedFilename = URLEncoder.encode(fileName, "UTF-8");設(shè)置文件名的中文編碼 response.addHeader("Content-Type","application/octet-stream;charset=utf-8");//這里也設(shè)置了相同的編碼格式 response.setHeader("Content-disposition", "attachment; filename="+encodedFilename);
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
vue-cli項目中使用公用的提示彈層tips或加載loading組件實例詳解
這篇文章主要介紹了vue-cli項目中使用公用的提示彈層tips或加載loading組件,本文通過實例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2018-05-05vue.js2.0 實現(xiàn)better-scroll的滾動效果實例詳解
better-scroll 是一個移動端滾動的解決方案,它是基于 iscroll 的重寫。better-scroll 也很強大,不僅可以做普通的滾動列表,還可以做輪播圖、picker 等等,下面通過本文給大家介紹vue.js2.0 實現(xiàn)better-scroll的滾動效果,感興趣的朋友一起看看吧2018-08-08關(guān)于vue2使用element?UI中Descriptions組件的遍歷問題詳解
最近element-ui遇到了很多坑,下面這篇文章主要給大家介紹了關(guān)于vue2使用element?UI中Descriptions組件的遍歷問題,文中通過實例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-02-02淺析Vue下的components模板使用及應(yīng)用
這篇文章主要介紹了Vue下的components模板的使用及應(yīng)用,本文通過代碼介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下2019-11-11