文件上传和下载进度以及取消

5/16/2023 文件上传

对于大文件上传和下载,为了提升用户的体验,我们通常会添加进度条以显示上传和下载进度,同时支持用户在上传和下载中对请求进行取消。

我主要采用XMLHttpRequestaxios分别实现上诉功能。

# 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); // 获取进度
  }
};
1
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 为我们提供了onUploadProgressonDownloadProgress属性来实现上传和下载的进程查看

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); // 获取进度
    }
  },
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

对于onUploadProgressonDownloadProgress的回调参数与XMLHttpRequest是一致的

# 取消上传

对于XMLHttpRequest,可以直接调用其abort方法来取消请求,也就实现了上传的取消

const xhr = new XMLHttpRequest();
// 取消请求
xhr.abort();
1
2
3

对于 axios,老版本可以使用axioscancelToken来实现请求的取消

// 创建取消令牌的生成器对象
const cancelToken = axios.cancelToken;
// 从令牌中获取对象
const source = cancelToken.source();
// 发送请求
axios({
  url,
  method: 'POST',
  cancelToken: source.token;
})
// 取消请求
source.cancal("请求取消")
1
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();
1
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("请求已取消"));
  }
});
1
2
3
4
5
6
7
8
9

使用 AbortController 可以方便的取消正在进行的请求,避免不必要的资源消耗。

Last Updated: 1/21/2025, 10:16:53 AM