一、考虑兼容性ie浏览器
代码示例如下:
// 文件下载,后端返回文件流方式,兼容ie浏览器
const fileDownload = (fileName, params) => {
showLoading();//出现load动画
Api.get(xxxfiledownloadurl, {
params,
responseType: 'blob',
headers: {
isShowLoading: false, // 不要全局loading,使用自定义的loading,需要在封装的Api拦截方法中设置一下,略...
}
})
.then(res => {
const data = res.data;
let content;
let reader = new FileReader();
reader.onload = event => {
content = reader.result;
try {
const obj = JSON.parse(content);
if (obj && obj.code && obj.message) { // 因为blob返回的错误catch接收不了,只能使用FileReader读取再做出提示
return alert(`${obj.code}-${obj.message}`);
}
else if (window.navigator && window.navigator.msSaveBlob) {
// 兼容ie浏览器
return window.navigator.msSaveBlob(data, fileName);
}
else {
reader.readAsDataURL(data);
}
}
catch (err) {
return saveAs(data, fileName)
}
}
reader.saveAsText(data)
}).catch(err => {
// 正常的接收到的错误提示--根据打印的内容筛选出需要进行错误的提示内容,略...
console.log(Object.values(err));
})
.finally(() => {
hideLoading();//隐藏load动画
})
}
// 注意: 如果是post请求:
Api.post(xxxfiledownloadurl, params对象, {
responseType: 'blob',
headers: {
isShowLoading: false, // 不要全局loading,使用自定义的loading,需要在封装的Api拦截方法中设置一下,略...
}
})
.then(res=>{略...}).catch(err=>{略...});
二、如果不考虑兼容性问题
1.await+TextDecoder+arrayBuffer
也可以使用下面downloadExcel方法:
思路:
- 一般情况下如果成功下载,data直接返回文件流数据,它无法使用parse解析;
- 如果下载报错,返回的json字符串就可以使用parse解析成对象;
- 巧用trycatch,try中获取报错信息,catch中下载文件。
import _ from 'lodash';
import axios from 'axios';
async function blobToJson(blob) {
const decoder = new TextDecoder('utf-8'); // 创建一个TextDecoder实例来解码UTF-8编码的文本
const text = decoder.decode(await blob.arrayBuffer()); // 将Blob转换为ArrayBuffer,然后解码为文本
return JSON.parse(text); // 解析文本为JSON对象
}
async function downloadExcel(params) {
const res = await axios({
url: '/download/file/xxx',
method: 'get',
responseType: 'blob',
params,
});
const data:any = _.get(res,'data');
const fn = () => {
const url = window.URL.createObjectURL(data);
const elink = document.createElement('a');
elink.href = url;
elink.target = '_self';
elink.setAttribute('download', 'xxx.xlsx');
elink.style.display = 'none';
document.body.appendChild(elink);
elink.click();
document.body.removeChild(elink);
}
try {
const result = await blobToJson(data);
if (result && result.message) {
const $message = window['$message'];
$message.error(result?.msg || result?.message);
}
}
catch (err) {
fn();
}
return res;
}
2.普通Promise+fileReader
思路:
- 如果后端有控制responseHeader的content-disposition,成功返回文件流就有在responseHeader中添加content-disposition;
- 下载接口报错就没有在responseHeader中添加content-disposition;
- 如果responseHeader中没有content-disposition,就利用FileReader获取错误信息,否则就直接下载文件。
function downloadExcel(params) {
return axios({
url: prefix + '/download/file/xxx',
method: 'post',
responseType: 'blob',
data: params,
})
.then((res) => {
if (!res.headers['content-disposition']) {
const fileReader = new FileReader();
fileReader.readAsText(
new Blob([res.data || ''], { type: 'application/octet-stream' }),
'utf-8'
);
fileReader.onload = () => {
const result = JSON.parse(fileReader.result || '{}');
const $message = window['$message'];
$message.error(result?.msg || result?.message);
};
return;
}
const url = window.URL.createObjectURL(res.data);
const elink = document.createElement('a');
elink.href = url;
elink.target = '_self';
elink.setAttribute('download', 'xxx.xlsx');
elink.style.display = 'none';
document.body.appendChild(elink);
elink.click();
document.body.removeChild(elink);
});
}