上传文件往往需要走这么几个步骤:
1.判断类型
2.判断大小
3.拿到上传地址
4.往地址推送文件
当文件符合要求,且比较小的时候,成功率是比较高的,但如果文件大,上传的路径又复杂,再加上网络问题,失败率就会陡增,这时候该怎么改善体验呢?
很典型的一个场景,迅雷、百度云盘,这些都是允许用户主动暂停,或者允许进行一半失败了接着进行。
即使不是这样的场景,我们也希望达到两个目的:
一、大文件不因中途出了一点点问题就彻底失败。
二、上传过程可以获知当前进度,让用户知道传了多少,还要等待多久。
所以,提高上传成功率,让用户对较长的上传过程有所感知,就是我们优化的目标。
1. 第一步:文件分片
通常情况下,file文件来源于Input元素选择文件后返回的FileList对象,或者自由拖放操作生成的DataTransfer对象。
File的本身是没有slice的方法的,不过它从Blob的接口继承了该方法。
接下来,我们进行切割文件
file.slice(0,2*1024*1024)
这是将文件从开头切割到2M大小的地方,然后我们只需要确定分片的文件阈值,例如50MB的文件就开始进行分片,然后每一片的大小,这里需要进行一个取舍,在片数较少的情况下,保证单片的传输效率
第二步:分片上传
let index = 0,// 记录当前已经切割的分片大小
fileList= []; // 请求参数列表
while (index < file.size) {
// 这里向参数列表添加参数项,具体可由前后端协定
fileList.push({
filename: index / SIZE, fileChunk: file.slice(index, index + SIZE), 'content-length': file.size % SIZE });
index += SIZE;
}
在这里构造好自己的参数类型后,向后端发起请求。
如果后端支持并发,那就直接发请求,如果不行,就用async/await,配合Promise.all()来一片片的阻塞上传
第三步:容错处理
1.用户手动取消的场景
2.断点续传的场景,例如网页刷新,网络异常等,这个时候,根据记录的分片索引,重新开始上传文件,这样就不需要用户从头开始上传,大大优化用户体验。
3.失败重试的场景,因为每一片文件都是一次请求,难免因为网络情况会导致部分切片失败,这里根据是阻塞上传还是并发上传进行一点区别处理。如果多次失败,就给用户进行提示,因为多次上传失败,要么是因为这个ip的请求间隔过短,被服务器锁了请求,或者服务器崩了,亦或者用户网络不行,这个时候就把选择权让渡给用户本身即可。
写在最后
如果是单个固定请求的重试,不必传参数,写到封装的方法中即可,但分片上传时每次的参数都不同,就需要传入参数。
重试的是一个新的请求,不是上次请求的结果,所以需要封装为一个函数,每次调用重新发起,而不是将上次请求的结果再次传入。