开启掘金成长之旅从一个小小的上传进度显示开始

303 阅读5分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第1天,点击查看活动详情

前言

由于项目需求,需要使用antd 的Upload 组件用来上传文件,在上传文件的同时需要提供上传进度显示,以及可以取消请求操作,本以为参考Api文档可以很快的将需求完成,没想到碰到了一些预想不到的问题。

上传进度显示

查找antd Upload Api 文档可以看到,需要的上传进度信息,通过onChange 方法可以获取到,然后我按照文档配置完成以后,发现onChange 方法里面根本拿不到event 对象信息。

目前代码结构大概如下:

然后没有办法,拿不到对应的event,也不知道哪里出现问题了,只能去找antd 的源码,看看问题出现在哪里。

Upload 源码

在源码中,根据onChange 方法,我们先找到onInternalChange 方法,在这个方法中,会接收event 这个对象,那么我们现在去寻找onInternalChange 这个方法,看它会在哪个步骤里面接收到event 对象。

然后,我们会找到onProgress 这个方法,在这个方法中,我们会看到,onInternalChange 这个方法会接收event对象,所以,我们还需要接着向上查找。最终我们发现,onProgress 这个方法会连带着其他参数和方法一起传给RcUpload 组件,所以,我们需要去寻找RcUpload 组件的源码,看最终这个方法是如何执行的。

RcUpload 源码

在源码中,我们可以看到,onProgress 这个方法,会放在requestOption 这个对象里面,然后传递给customRequest || defaultRequest ,那么,至此,就可以很明显的看出来,为什么在Upload 组件onChange 方法中,无法拿到event 对象了,因为我这边是使用的customRequest,在customRequest中,并没有调用requestOption.onProgress 方法,所以我们在之前的代码中无法从onChange中拿到event。

既然到了这里,那我们也可以顺便看一下defaultRequest 这个默认的方法,是如何将event 对象进行传输的。

可以看到,在默认的请求方法中,会创建一个XMLHttpRequest 对象,然后将onProgress 方法,在xhr 实例的upload对象的onprogress 方法中拿到event。所以,最终的event 其实是从xhr 原生的ajax 对象上获取得到的。

XMLHttpRequest.upload

XMLHttpRequest.upload 属性返回一个 XMLHttpRequestUpload对象,用来表示上传的进度。这个对象是不透明的,但是作为一个XMLHttpRequestEventTarget,可以通过对其绑定事件来追踪它的进度。

事件相应属性的信息类型
onloadstart获取开始
onprogress数据传输进行中
onabort获取操作终止
onerror获取失败
onload获取成功
ontimeout获取操作在用户规定的事件内未完成
onloadend获取完成(不论成功与否)
ProgressEvent

ProgressEvent.lengthComputable

是一个 Boolean (en-US) 标志,表示底层流程将需要完成的总工作量和已经完成的工作量是否可以计算。换句话说,它告诉我们进度是否可以被测量。

ProgressEvent.loaded

是一个 unsigned long long 类型数据,表示底层流程已经执行的工作总量。可以用这个属性和 ProgressEvent.total 计算工作完成比例。当使用 HTTP 下载资源,它只表示内容本身的部分,不包括首部和其它开销。

ProgressEvent.total

是一个 unsigned long long 类型数据,表示正在执行的底层流程的工作总量。当使用 HTTP 下载资源,它只表示内容本身的部分,不包括首部和其它开销。

Axios 封装问题

onUploadProgress

通过上面的代码可以知道,Upload 组件默认请求是使用的XMLHttpRequest.upload,但是项目中使用的请求方法使axios,所以要想完成需求,还需要去查看axios 的api,看是否有包含获取上传信息的方法或属性。

通过查找Api文档我们可以发现,onUploadProgress 这个方法可以获取上传信息,所以,我们在发送请求的时候,将这个属性方法给配置上去,就可以用来处理对应的文件上传信息。

取消请求

查询axios api 文档,我们可以知道,取消axios请求有两种方法:

  • AbortController
  • CancelToken

这次主要是通过AbortController,这个属性方法来对axios请求进行取消。

注意:这些属性都是需要进行配置在axios 的config 里面的。

上传请求测试

由于项目中,后端接口还没有编写完成,所以我们自己使用nodejs 编写了一个简易的文件上传接口。

具体代码如下:

const express = require("express");
const app = express();
const bodyParser = require("body-parser");
const multer = require("multer");

app.use(express.static("public"));
app.use(bodyParser.urlencoded({ extended: false }));

const myStorage = multer.diskStorage({
  destination(req, file, callback) {
    console.log(file);
    callback(null, "./public");
  },
  filename(req, file, callback) {
    callback(null, file.originalname);
  },
});

const save = multer({ storage: myStorage });

app.get("/aa/index", (req, res) => {
  res.send("hello");
});

app.post("/aa/upload", save.single("uploadData"), (req, res) => {
  console.log(req.body);
//   res.status(400);
  res.json(-1);
});

app.listen(5000, () => {
  console.log("服务器已经启动");
});

总结

通过对这次需求的完成和分析,发现在编写需求的时候,有一些觉得理所应当的东西,其实应该去根据源码探究一下,追本溯源,看看最终的实现结果会落到何地何处,这样也可以多了解自己之前不太清楚或者不太明白的技术或原理。

参考文档:

axios-http.com/zh/docs/can…

ant.design/components/…

github.com/ant-design/…

gitee.com/mirrors/upl…

developer.mozilla.org/zh-CN/docs/…

blog.csdn.net/peilinll/ar…