这是我参与11月更文挑战的第3天,活动详情查看:2021最后一次更文挑战
代码自取,欢迎评论区沟通交流
一.Fetch是什么?
二.Fetch下载文件的过程
- 既然是请求,那么一定会涉及到两个东西,请求和响应。
- 接下来我们就分析下请求和响应
- 请求:通常下载文件请求就是常见的get和post请求
- 响应:Fetch API的 Response 对象就是响应的数据,也是本次需要处理的对象。用到的属性有
- Response.headers[3]:响应头信息,此处用来获取文件的名字
filename = decodeURIComponent(res.headers.get("content-disposition").replace("attachment;filename=", ""));
- Response.ok[4]:布尔值,是否响应成功
if(res.ok){
return res
}else{
// 错误处理
}
- Response.body[5]:响应的内容,ReadableStream[6]类型,通过
getReader()[7]方法创建一个ReadableStreamDefaultReader,按顺序读取。读取完成后,需要创建一个新的ReadableStream,用于生成Response,进行传递res,方便后续处理
const readStream = reader => new ReadableStream({
start(controller) {
return push();
function push() {
return reader.read().then((res) => {
const { done, value } = res;
if (done) {
controller.close();
}
controller.enqueue(value);
return push();
});
}
},
});
- 生成的ReadableStream用于返回一个新的Response,进行传递处理
三.转化成blob,以下方法等同于res.blob()
- 推荐使用res.blob(),如果正常运行,直接使用即可,使用方法等同于res.json()。这里是遇到res.blob()偶现解析不成功。
const resBlob = (reader, data, type) => new Promise((resolve) => {
function push() {
reader.read().then(({ done, value }) => {
data.push(value);
done ? resolve(new Blob(data, { type })) : push();
});
}
push();
});
四.保存文件
- 通过FileReader[8]读取刚刚的blob数据,等到loadend事件,读取完成,进行下载操作
reader.readAsText(blob): 开始读取指定的blob内容
const savingFile = (blob, fileName) => {
const reader = new FileReader();
reader.addEventListener('loadend', () => downloadBlob(blob, fileName));
reader.readAsText(blob);
};
五.下载文件
- 传入blob和文件名,进行下载操作
const downloadBlob = (blob, fileName) => {
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.target = '_blank';
a.style.display = 'none';
document.body.appendChild(a);
a.download = fileName;
a.click();
window.URL.revokeObjectURL(url);
document.body.removeChild(a);
};
六.使用举例
- 设置参数
- credentials:是否携带cookie
- headers:请求头,请求的格式是什么
const option = {
credentials: 'include',
headers: new Headers({
'Content-Type': 'application/x-www-form-urlencoded',
}),
};
// 下载
// payload:fetch请求的参数
const download = (payload, filename = "默认文件名.xlsx") =>
fetch(`request/url?${qs.stringify(payload)}`, option)
.then((res) => {
// 设置文件名,从响应头中获取
filename = decodeURIComponent(res.headers.get("content-disposition").replace("attachment;filename=", ""));
return new Response(readStream(res.body.getReader()));
})
.then((res) =>resBlob(res.body.getReader(), [], res.headers.get("Content-Type")))
.then((response) => savingFile(response, filename))
.catch((error) => message.warning(`${error.message}请再次尝试`));