In vue, use axios to get the back end, get the binary stream, download the file interface, and fail to get the attribute in headers: content disposition

Posted by Zup on Sat, 05 Mar 2022 01:11:28 +0100

Get binary stream file api writing

The back end here is a project written using the Spring Boot framework

I use the encapsulation here and then call the way:
First, write a tool class:
The name is downloadrequest js
The code notes are as follows:

import axios from "axios";
/**
 * api encapsulation for file download
 */

// Create an axios object
const service = axios.create({
  // Requested base path
  baseURL: "http://localhost:8082/api/v1",
  // Add the type of response returned
  responseType: "arraybuffer",
});

// request interceptor 
service.interceptors.request.use(
  (config) => {
    // Get the user's token and add it to the request header (here is the user token in the session)
    config.headers["Authorization"] = window.sessionStorage.getItem("tokenStr");
    return config;
  },
  (error) => {
    // Print error message
    console.log(error);
  }
);

// Response interceptor
service.interceptors.response.use(
  (resp) => {
    console.log(resp);
    // Get response header information
    const headers = resp.headers;
    // Judge whether the data in the request header is json format data
    let reg = RegExp(/application\/json/);
    if (headers["content-type"].match(reg)) {
      // If yes, use this function to parse
      resp.data = unitToString(resp.data);
    } else {
      // If not, use the installed third-party plug-in to resolve
      let fileDownload = require("js-file-download");
      // Gets the string returned in the response
      //   console.log(headers["content-disposition"]);
      let fileName = headers["content-disposition"]
        .split(";")[1]
        .split("filename=")[1];
      let contentType = headers["content-type"];
      // Use the following method to parse the data of the response
      fileName = decodeURIComponent(fileName);
      fileDownload(resp.data, fileName, contentType);
    }
  },
  (error) => {
    console.log(error);
  }
);

// Replace json format data
function unitToString(unitArray) {
  let encodedString = String.fromCharCode.apply(
    null,
    new Uint8Array(unitArray)
  );
  let decodedString = decodeURIComponent(escape(encodedString));
  return JSON.parse(decodedString);
}

// Edit the api interface (if you use this, please comment out the top [baseURL], and then directly introduce the downloadRequest method in the vue file to call directly)
// let base = "http://localhost:8082/api/v1";
// export const downloadRequest = (params) => {
//   return service({
//     method: "GET",
//     url: `${base}/employee/basic/export`,
//     params,
//   });
// };

// export
export default service;

The method I use is modular, which is convenient for secondary development and maintenance in the later stage. Therefore, I set up a special API js file and introduced the tool classes I encapsulated
The code notes are as follows:

import service from "../utils/download";
/**
 * Employee interface api
 */
/**
 * Export employee table
 * @param {*} params
 * @returns
 */
export const downloadRequest = (data) => {
  return service({
    method: "GET",
    url: `/employee/basic/export`,
    data,
  });
};

Finally, in the This method is introduced into the vue file and called without passing parameters
The code is as follows: (because there are many codes in my original file, I use the initialized. vue file)

<template>
  <div class="EmpAdv">
   <el-button type="success" icon="el-icon-download" @click="expostData"
          >Export data</el-button
        >
  </div>
</template>

<script>
import { downloadRequest } from "../../api/emplotee";
export default {
  name: "EmpAdv",
  data() {
    return {};
  },
  created() {},
  methods: {
  	// Export employee information
    expostData() {
      downloadRequest();
    }
  }
};
</script>

<style scoped></style>

I have a problem here, that is, when the encapsulated tool class processes the data of the response, it cannot find the 'content disposition' in the headers

The specific problems are described as follows:

Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'split')
    at __webpack_exports__.default (download.js?989b:43:1)


Problem description

It shows that there is an error here. After a careful look, my backend did not pass this parameter
But I went back and checked it, and it did pass
Then I found that this attribute exists in the browser debugger network, but it is not printed in the response interceptor. It's very evil!!

Causes of problems

In cross domain (CORS) requests, the method getResponseHeader() in the XMLHttpRequest object encapsulated by axios can only obtain six basic fields by default: cache control, content language, content type, Expires, last modified and Pragma. Therefore, you need to obtain other attributes, and you need to specify the content disposition attribute in access control expose headers when the backend responds.
Therefore, this is a back-end pot, which is not configured. For this problem, go to the back-end and ask him to configure it in the response.

Back end solutions

Configure the release attribute under the binary stream download method in the controller layer:

response.setHeader("Access-Control-Expose-Headers", "content-disposition");

Topics: Java Spring Spring Boot Vue