How to get the file name in the blob return stream when the front-end vue downloads a file

Posted by WLC135 on Mon, 07 Mar 2022 10:35:55 +0100

I wrote an article a long time ago that the front-end vue uses blob objects to download files. Some people believe me privately. If the back-end return flow fails, how can I get the json object given? How can the stream obtained by the front end obtain the original file name? In fact, after that article, I have optimized these two problems, so I have this article.

First, to solve the first problem, if the file stream can be obtained normally, the front end will accept it as a blob object. On the contrary, the back end will generally send a json object to tell you the failure and the reason for the failure. At this time, the json object will also generate a file due to the declaration of responseType: 'blob' in the request, but the file content is garbled. At this time, We have to use FileReader readAsText():

See the MDN official document about FileReader for details Description of readastext(): https://developer.mozilla.org/zh-CN/docs/Web/API/FileReader/readAsText . So the last time the code was modified, it looks like this:

<el-button @click="exportExcel()">export</el-button>

<script>
methods: {
        exportExcel(){
            var params={
                XX:xx//Additional request body to carry
            }
            this.$axios.get('/XX/XX',{
                params: params,
                responseType: 'blob'   //Set first responseType The field format is blob
            }).then(res => {
                if(res.type=="application/json"){
                       let reader = new FileReader();
                       reader.onload = e =>this.$alert(JSON.parse(e.target.result).xxxx);   //xxxx Is the field name
                       reader.readerAsText(res);
                }else{
                       let blob = new Blob([res], {type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8"}); // by blob Set the file type here with.xlsx take as an example
                       let url = window.URL.createObjectURL(blob); // Create a temporary url point blob object
                       let a = document.createElement("a");
                       a.href = url;
                       a.click();
                       // Release this temporary object url
                       window.URL.revokeObjectURL(url); 
                   }
             });
        },
     }
</script>      

Then, for the second question, how can the stream obtained by the front end obtain the original file name? There are two solutions that need the cooperation of back-end colleagues. One is to have a file list (table) before clicking the Export button. The front-end already knows the location of the file, and then passes the file path to the back-end, which takes it from the back-end to the specified directory and then passes it to you. When uploading a file, the back end will rename the file (8-bit random string + original file name) to prevent duplicate file names. If you know the path, you can directly intercept the field after the last "/" in the file path as the file name.  

<el-button @click="exportExcel()">export</el-button>

<script>
methods: {
        exportExcel(){
            var params={
                filePath:this.filePath
            }
            this.$axios.get('/XX/XX',{
                params: params,
                responseType: 'blob'   //Set first responseType The field format is blob
            }).then(res => {
                if(res.type=="application/json"){
                       let reader = new FileReader();
                       reader.onload = e =>this.$alert(JSON.parse(e.target.result).xxxx);   //xxxx Is the field name
                       reader.readerAsText(res);
                }else{
                       let blob = new Blob([res], {type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8"}); // by blob Set the file type here with.xlsx take as an example
                       let url = window.URL.createObjectURL(blob); // Create a temporary url point blob object
                       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();
                       // Release this temporary object url
                       window.URL.revokeObjectURL(url); 
                   }
             });
        },
     }
</script> 

The other is that the back-end colleagues first transform the request header and add the content disposition field to the request header returned by the front-end.

response.reset();
response.setContentType("application/application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8"); //Set the file type here with.xlsx take as an example
//Set the original file name of the file. If the file name contains Chinese, it needs to be decoded, otherwise garbled code will appear
response.setHeader("Content-Disposition", "attachment;fileName=" + URLEncoder.encode(fileName, "utf-8"));
// This step is critical and needs to be added to the request header returned to the front end Content-Disposition field
response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");  

Request header in console network

Then some people get stuck in this place and say that they can't get the value of content disposition using res.headers['content-disposition ']. This is because these people have configured the axios interceptor and wrote relevant processing in the interceptor, resulting in not returning all response information. We are in the code above The res in the then() callback function must be processed by the axios interceptor before returning. If the axios interceptor is not set, it can be obtained directly through res.headers['content-disposition '].

// Add response interceptor
axios.interceptors.response.use(response=>{
    // Do something about the response data
    return response.data; //Here, only those in this response data It's back, so I can't get it headers,If you want all the information, just return response;
  }, error=>{
    // Do something about response errors
    return Promise.reject(error);
});

 

Topics: axios