前情提要:公司要求大文件分片上传,第一次听到的时候有点懵,文件要怎么分片?
网上查了一下,发现一个很简单的方法,直接slice分割(O.O),有种大开眼界的感觉。就写一个基础的文件分片上传 + antd.Upload.customRequest以供参考吧。
还是要说,不考虑代码优雅,反正能用就行。
antd.Upload.customRequest的基础使用方法这里有写,就不多赘述了,直接用吧。
customRequest: e => {
// file里包括了整个文件信息
const { file } = e;
// 5 * 1024 *1024 = 5MB,每个分片5M,即文件大于5M的时候需要分片上传
const SLICE_SIZE = 5242880;
// 小于5M的情况直接上传文件
if (file.size < SLICE_SIZE) {
// 如何上传文件,需要大家各显神通,后端要啥数据咱就给啥数据呗。
// 我直接用了jquery的ajax
}
else {
// 后端据说是参考了阿里云的oss上传,分片上传第一步需要获取uploadId
// 在之后上传分片信息的时候,需要加上参数uploadId,用来标记分片,最后一步的时候把上传的分片组装起来。
// 这里就忽略忽略,不同上传方案有不同的处理方法。
// 直接进行分片,每个分片对应一个请求
const promiseList = getPromiseList(uploadId, e, SLICE_SIZE,);
// 第二步:执行分片上传
Promise.all(promiseList).then(res => {
// promiseList中没有返回false的,代表上传成功
if (res.every(item => item)) {
// 第三步:将所有分片合为一个文件,我需要发个请求将每次分片上传返回的信息按照指定格式拼接起来传给后端。
// 最后怎么处理,不同上传方案有不同的处理方法,这里也忽略忽略
// 记得调用e.onSuccess 或者e.onError告诉Upload上传成功或失败
}
else {
e.onError("上传失败")
}
}).catch(err => {
e.onError("上传失败")
})
}
}
/**
* @description: 进行分片,每个分片对应一个请求
* @param {string} uploadId 分片上传文件对应的唯一标识符
* @param e customRequest参数
* @param {number} sliceSize每一片分片大小
* @return 文件分片上传请求列表
*/
const getPromiseList = (uploadId, e, sliceSize) => {
// file里包括了整个文件信息
const { file } = e;
// 文件分片列表
const splitList = splitFile(file, sliceSize);
// 请求列表
const promiseList: any = [];
// 每个分片上传进度
let uploadToal = new Array(splitList.length).fill(0)
// 每一个文件分片发送一个请求
splitList.forEach((item, index) => {
promiseList.push(new Promise((resolve, reject) => {
return $.ajax({
url, // 上传地址
type, // get、post、put等类型
// 上传参数,我就直接把文件信息不做处理放到请求参数里了
data: item,
cache: false, //上传文件不需要缓存
processData: false, // 告诉jQuery不要去处理发送的数据
contentType: false, // 告诉jQuery不要去设置Content-Type请求头
// 监听上传进度
xhr: function () {
var xhr = new XMLHttpRequest();
//使用XMLHttpRequest.upload监听上传过程,注册progress事件
xhr.upload.addEventListener('progress', function (progressEvent) {
// 当前分片的上传进度
uploadToal[index] = progressEvent.loaded;
// 文件总的上传进度
const percent = Math.round((uploadToal.reduce((pre, t) => pre + t, 0) * 100) / file.size);
// 上传进度传给upload组件
e.onProgress({ percent });
})
return xhr;
},
// 完成请求后,返回需要的信息
// 我需要处理XMLHttpRequest对象响应头中的信息,所以在complete中执行resolve,如果请求返回了数据,需要在success中处理
// 不同上传方案处理方式不一样,代码仅供参考
complete: (xhr) => {
const Etag = xhr.getResponseHeader("Etag");
resolve(Etag);
},
// 上传失败,返回false
error: (err) => {
reject(false);
}
})
}))
})
return promiseList
}
/**
* @description: 文件分片
* @param file 整个文件信息
* @param {number} sliceSize每一片分片大小
* @return 返回每片信息
*/
const splitFile = (file, sliceSize) => {
// 分片数量,向上取整
const splitNum = Math.ceil(file.size / sliceSize);
// 分片列表
const splitList: any = [];
// 第几片分片
let current = 0;
while (current < splitNum) {
const start = current * sliceSize;
const end = Math.min(file.size, start + sliceSize);
// 直接slice文件
const part = file.slice(start, end);
splitList.push(part);
current++;
}
return splitList;
};
参考
https://blog.csdn.net/weixin_43726152/article/details/136722114