对于大文件上传和下载,为了提升用户的体验,我们通常会添加进度条以显示上传和下载进度,同时支持用户在上传和下载中对请求进行取消。
我主要采用XMLHttpRequest
和axios
分别实现上诉功能。
# XMLHttpRequest 获取进度
在新版本的XMLHttpRequest
对象中,可以通过监听xhr.upload.onprogress
事件来获取文件上传的进度,通过xhr.onprogress
获取下载进度。
const xhr = new XMLHttpRequest();
// 上传进度
xhr.upload.onprogress = function (e) {
// e.lengthComputable是一个布尔值,表示当前上传的资源是否具有可计算的长度。
if (e.lengthComputable) {
// e.loaded 已传输的字节
// e.total 需要传输的总字节
const percentComplete = Math.ceil((e.loaded / e.total) * 100); // 获取进度
}
};
// 下载进度
xhr.onprogress = function (e) {
// e.lengthComputable是一个布尔值,表示当前上传的资源是否具有可计算的长度。
if (e.lengthComputable) {
// e.loaded 已传输的字节
// e.total 需要传输的总字节
const percentComplete = Math.ceil((e.loaded / e.total) * 100); // 获取进度
}
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
其中onprogress
回调中会返回一参数,该参数主要包含 3 个属性
- lengthComputable: 一个布尔值,表示当前上传的资源是否具有可计算的长度
- loaded: 已传输的字节
- total: 需要传输的总字节
我们就可以通过这些属性来获取文件上传的进度
# axios 获取进度
axios 为我们提供了onUploadProgress
和onDownloadProgress
属性来实现上传和下载的进程查看
axios({
url: "url",
data: formData,
method: "POST",
onUploadProgress: (e) => {
if (e.lengthComputable) {
const percentComplete = Math.ceil((e.loaded / e.total) * 100); // 获取进度
}
},
onDownloadProgress: (e) => {
if (e.lengthComputable) {
const percentComplete = Math.ceil((e.loaded / e.total) * 100); // 获取进度
}
},
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
对于onUploadProgress
和onDownloadProgress
的回调参数与XMLHttpRequest
是一致的
# 取消上传
对于XMLHttpRequest
,可以直接调用其abort
方法来取消请求,也就实现了上传的取消
const xhr = new XMLHttpRequest();
// 取消请求
xhr.abort();
2
3
对于 axios,老版本可以使用axios
的cancelToken
来实现请求的取消
// 创建取消令牌的生成器对象
const cancelToken = axios.cancelToken;
// 从令牌中获取对象
const source = cancelToken.source();
// 发送请求
axios({
url,
method: 'POST',
cancelToken: source.token;
})
// 取消请求
source.cancal("请求取消")
2
3
4
5
6
7
8
9
10
11
12
新版本我们可以使用AbortController
控制器对象来实现请求的取消
// 创建一个新的 AbortController 对象实例。
const abortController = new AbortController();
// 返回一个 AbortSignal 对象实例,它可以用来 with/abort 一个 Web(网络)请求。
const { signal } = abortController;
// 我们将 AbortSignal 作为一个选项传递进入请求的选项对象中(下面的 {signal})。这将 signal 和 controller 与 axios 请求相关联
axios.get(url, { signal });
// 中止一个尚未完成的 Web(网络)请求。这能够中止 fetch 请求及任何响应体的消费和流。
abortController.abort();
2
3
4
5
6
7
8
AbortController 对象实例会返回一个 AbortSignal 对象实例(signal),我们将 AbortSignal 作为一个选项传递进入请求的选项对象中这将 signal 和 controller 与 axios 请求相关联,并且允许我们通过调用 AbortController.abort() 去中止它,这样就实现了请求的取消
需要注意的是如果请求已经发送出去,这是调用abort
是不会立即生效的,但是会在请求结束后将响应标记为aborted
,我们就能在响应器中进行判断
axios.interceptors.response.use((response) => {
if (response.status === 200) {
return response.data;
}
if (response.status === 0) {
return Promise.reject(new Error("请求已取消"));
}
});
2
3
4
5
6
7
8
9
使用 AbortController 可以方便的取消正在进行的请求,避免不必要的资源消耗。