遇到了个问题,后端直接返回表格的 Stream,在 ApiFox 里点下载出来的表格文件完全正常,但是在前端里导出的内容不行,会报错:Excel 无法打开文件“XXXX.xlsx”,因为文件格式或文件扩展名无效。请确定文件未损坏,并且文件扩展名与文件的格式匹配。

前端是这么写的:

axios.post(BASE_URL + "/output", JSON.stringify(realMultiFileResponseTypeList), {
    headers: {
        "Content-Type": "application/json"
    },
    responseType: 'blob'
}).then(function(response) {
    const {data, headers} = response
    const regex = /filename="(.*?)"/gm
    const fileName = regex.exec(headers['content-disposition']) ![1]
    console.log(fileName)

    const _utf = '\uFEFF';
    const blob = new Blob([data], {
        type: "application/octet-stream"
    });

    
    let url = URL.createObjectURL(blob);
    let dom = document.createElement('a')
    dom.href = url
    console.log(url)
    dom.download = fileName
    dom.style.display = 'none'
    document.body.appendChild(dom)
    dom.click()
    URL.revokeObjectURL(url)
    document.body.removeChild(dom)
}).
catch(function(error) {
    console.log(error);
});

这个也是很常见的导出,基本搜一搜 JavaScript 导出二进制 都是类似的内容,但是 XLSX 不适用。因为 XLSX 不同于 XLX 是个文件,它本质上是一个压缩文件,前端单纯地 URL.createObjectURL 没有办法实际生成 XLSX,所以需要 SheetJS 这个库。

SheetJS:https://docs.sheetjs.com/

安装就不赘述了,直接贴一下结果的代码:

import * as XLSX from 'xlsx';
...
axios.post(BASE_URL + "/output", JSON.stringify(realMultiFileResponseTypeList), {
    headers: {
        "Content-Type": "application/json"
    },
    responseType: 'blob'
}).then(async function(response) {
    const {data, headers} = response
    const regex = /filename="(.*?)"/gm
    const fileName = regex.exec(headers['content-disposition']) ![1] console.log(fileName)
    const _utf = '\uFEFF';
    const blob = new Blob([data], {
        type: "application/octet-stream"
    });

    const wb = XLSX.read(await blob.arrayBuffer(), {type: "array"});
    XLSX.writeFile(wb, fileName);
}).
catch(function(error) {
    console.log(error);
});

主要变化其实就是创建一个 Workbook,把 blob 的东西转成 ArrayBuffer 后塞给它,然后调用 XLSX.writeFile() 就行。这里就不需要再手动创建一个隐藏的 a 标签模拟点击再删除了,writeFile() 里本身写好了,调用就行(

另外,因为其中出现了 await,axios 的 then 需要带有 async