前端vue下載文件時blob返迴流中怎麼獲取文件名
我很久之前寫了一篇前端vue利用blob對象下載文件,有些人私信我,如果後端返迴流失敗,給出的json對象該怎麼獲得?前端獲取的流怎麼能獲取原文件名?其實在那篇文章之後,我就已經針對這兩個問題進行了優化,於是就有了這篇。
首先,針對第一個問題,如果能正常獲得文件流,前端則以blob對象承接,反之,一般後端會傳一個json對象告訴你失敗了以及失敗原因,這個時候json對象由於請求中responseType: ‘blob’的聲明之後,也會生成文件,但是文件內容是亂碼,這個時候,我們必須要用到FileReader.readAsText():
詳細見MDN官方文檔關於FileReader.readAsText()的描述://developer.mozilla.org/zh-CN/docs/Web/API/FileReader/readAsText。所以上次的程式碼經過改造是這樣子的:
<el-button @click="exportExcel()">導出</el-button> <script> methods: { exportExcel(){ var params={ XX:xx//額外需要攜帶的請求體 } this.$axios.get('/XX/XX',{ params: params, responseType: 'blob' //首先設置responseType欄位格式為 blob }).then(res => { if(res.type=="application/json"){ let reader = new FileReader(); reader.onload = e =>this.$alert(JSON.parse(e.target.result).xxxx); //xxxx為欄位名 reader.readerAsText(res); }else{ let blob = new Blob([res], {type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8"}); // 為blob設置文件類型,這裡以.xlsx為例 let url = window.URL.createObjectURL(blob); // 創建一個臨時的url指向blob對象 let a = document.createElement("a"); a.href = url; a.click(); // 釋放這個臨時的對象url window.URL.revokeObjectURL(url); } }); }, } </script>
然後,我們針對第二個問題,前端獲取的流怎麼能獲取原文件名?有兩種解決方案,都得需要後端同事的配合,一種是在點擊導出按鈕之前,一般這種就是有一個文件列表(表格),前端已經知道文件的位置,然後把文件路徑傳給後端,由後端到指定目錄下取到再傳給你。文件上傳的時候因為防止文件名重複,後端會對文件進行重命名(採取8位隨機字元串+原文件名),在你知道路徑的情況下,你就可以直接將文件路徑中最後一個”/”後欄位進行截取作為文件名。
<el-button @click="exportExcel()">導出</el-button> <script> methods: { exportExcel(){ var params={ filePath:this.filePath } this.$axios.get('/XX/XX',{ params: params, responseType: 'blob' //首先設置responseType欄位格式為 blob }).then(res => { if(res.type=="application/json"){ let reader = new FileReader(); reader.onload = e =>this.$alert(JSON.parse(e.target.result).xxxx); //xxxx為欄位名 reader.readerAsText(res); }else{ let blob = new Blob([res], {type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8"}); // 為blob設置文件類型,這裡以.xlsx為例 let url = window.URL.createObjectURL(blob); // 創建一個臨時的url指向blob對象 let a = document.createElement("a"); a.href = url; let filePath = this.filePath; let subFilePath = filePath.split('/'); a.download = subFilePath[subFilePath.length-1].substring(8); a.click(); // 釋放這個臨時的對象url window.URL.revokeObjectURL(url); } }); }, } </script>
另一種是後端同事先對請求頭進行改造,在給前端返回的請求頭中添加Content-Dispositon欄位。
response.reset(); response.setContentType("application/application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8"); //設置文件類型,這裡以.xlsx為例 //設置文件的原文件名,若文件名中含有中文則需要解碼,否則會出現亂碼 response.setHeader("Content-Disposition", "attachment;fileName=" + URLEncoder.encode(fileName, "utf-8")); // 這步很關鍵,需要在給前端返回的請求頭中添加Content-Disposition欄位 response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
f12控制台network中請求頭部分
然後有些人就是在這個地方卡住了,說利用res.headers[‘content-disposition’]取不到Content-Dispositon的值,這是因為這些人都配置了axios攔截器,而且在攔截器里寫了相關的處理導致沒有返回全部的響應資訊。我們在上面程式碼中.then()回調函數中的res返回前是都要經過axios攔截器處理的,如果沒有設置axios攔截器的是可以直接通過res.headers[‘content-disposition’]直接獲取到的。
// 添加響應攔截器 axios.interceptors.response.use(response=>{ // 對響應數據做點什麼 return response.data; //這裡只把這個響應里的data返回回來了,所以取不到headers,想要全部資訊就return response; }, error=>{ // 對響應錯誤做點什麼 return Promise.reject(error); });