1.基本方法:
注意:uploadArr,fileObject存在state中
const getFilePara = (option: any) => {
const fileSize = option.file.size;
let everyPieceSize: number = Math.pow(1024, 2);
const fileSlitLen = Math.ceil(fileSize / everyPieceSize);
return {
fileSlitLen,
everyPieceSize,
}
}
const preData = (option: any, uploadParam: any = {}, index: number) => {
const { fileSlitLen, everyPieceSize } = getFilePara(option);
const fileBlob: any = splitFile(option.file, everyPieceSize, index);
if (batchIndex > fileSlitLen - 1) return showError('分片超过限制!');
return {
fileBlob,
uploadData: uploadParam,
}
}
const getFormData = (params: any, option: any) => {
const formData = new FormData();
formData.append('FILE', params.fileBlob, option.file.name);
return formData;
}
const uploadFn = ({ step, urlParams, formData, headers, option, resumeFlag },index: number, uploadDataArr: any[], finishApiFn: () => void) => {
const { fileSlitLen } = getFilePara(option)
const getItem = (item:any)=>_.pick(item, ['KEY', 'NAME', 'ID']) || {}
return new Promise((resolve, reject) => {
Api.post(uploadUrl + urlParams, formData, {
headers
}).then((res: any) => {
let result: any = _.get(res, 'xxx') || {}
const fileO: any = {
...getItem(result),
PART_NUM: result.PART_NUM,
index,
success: true
}
uploadDataArr[fileO.index] = fileO
if (result.ID && result.PART_NUM) {
const successArr: any[] = _.filter(uploadDataArr, (item: any) => _.get(item, 'success')) || []
setProgressShow(true)
if (index > fileSlitLen - 1) return;
if (successArr.length === fileSlitLen) {
// 调用合并接口-略。。。注意合并成功后删除对应的uploadDataArr, 页面要展示的fileList要加入这个完成合并的文件数据
}
if (index === 0 && !resumeFlag) {
const start1 = 1
const end1 = fileSlitLen >= step + 1 ? step + 1 : fileSlitLen
runFn({ step, option, resumeFlag }, start1, end1, uploadDataArr, finishApiFn)
}
}
return resolve(fileO)
}).catch((err: any) => {
uploadDataArr[index] = {
...getItem(_.get(uploadDataArr, [0])),
PART_NUM: index + 1,
index,
success: false
}
if (!_.get(_.get(uploadDataArr, [0]), 'ID') && index === 0) {
showError('文件上传失败')
}
return reject(err)
}).finally(() => {
const errArr: any[] = _.filter(uploadDataArr, (item: any) => !_.get(item, 'success')) || []
const successArr: any[] = _.filter(uploadDataArr, (item: any) => _.get(item, 'success')) || []
const item: any = _.pick(_.get(successArr, [0]), ['KEY', 'NAME', 'ID']) || {}
const progressPercent = getProGressRate(successArr, fileSlitLen)
const fileProgressItem = {
...item,
errArr,
uploadDataArr,
progressPercent: progressPercent >= 100 ? 99.9 : progressPercent,
option,
}
if (successArr.length > 0) {
fileObject[item.KEY] = fileProgressItem
uploadArr = []
uploadArr = _.values(fileObject) || []
}
const con1 = index >= 0 && successArr.length > 0 && fileSlitLen === uploadDataArr.length
const con2 = successArr.length === 0 && index === 0
if (con1 || con2) {
!resumeFlag && finishApiFn()
}
})
})
}
2.新增上传
/**
* 新增上传--当第一次成功后执行: 每次并发step次, 如果step为1相当于严格顺序上传
* @param param0 {step: 每次并发数量,option: 文件原始数据,resumeFlag: 判断是否并发}
* @param start 开始下标
* @param end 结束下标
* @param uploadDataArr 分片结果数据:包括成功和失败的
* @param finishApiFn
*/
function runFn(
{ step, option, resumeFlag }: { step: number, option: any, resumeFlag: boolean },
start: number,
end: number,
uploadDataArr: any[],
finishApiFn: () => void
) {
let fnArr: any[] = [];
const successItem: any = _.pick(_.get(uploadDataArr, [0]),['KEY', 'ID', 'NAME']) || {};
const { fileSlitLen } = getFilePara(option);
for (let i = start; i < end; i++) {
const partNumber = i + 1;
const fileO = {
...successItem,
PART_NUM: partNumber,
index: i,
}
const params: any = preData(option, fileO, i);
const formData = getFormData(params, option);
const p1: any = _.pick(fileO, ['KEY', 'ID', 'PART_NUM']) || {};
const urlParams = queryParams(p1);
const f1 = uploadFn(
{ step, urlParams, formData, headers, option, resumeFlag },
i,
uploadDataArr,
finishApiFn
);
fnArr.push(f1)
}
Promise.all(fnArr)
.finally(() => {
let start1 = end > fileSlitLen ? fileSlitLen - (end - start) : end;
let end1 = start1 + step >= fileSlitLen ? fileSlitLen : start1 + step;
// 结束下标必须比开始下标大,至少差值为1
const con1 = start1 !== start && start1 >= 0 && start1 <= fileSlitLen - 1 && end1 - start1 >= 1 && end1 <= fileSlitLen;
if (!con1) {
const errFlag = _.some(uploadDataArr, item => !_.get(item, 'success'));
if (errFlag) showError('文件上传失败');
return;
}
fnArr = [];
runFn({ step, option, resumeFlag }, start1, end1, uploadDataArr, finishApiFn);
})
}
3.续传
/**
* 续传--并发|顺序
* @param param0
* option 文件数据
* step 并发次数
* errArr 没有上传成功的数据
* @param promiseNum 第几轮并发(点击续传按钮, 这个值传1, 每次并发接口调完+1)
* @param start 开始下标
* @param end 结束下标
* @param uploadDataArr 用来保存上传分片结果的数据:包括成功和失败的
* @param finishApiFn 回调方法
*/
const runContinueFn = (
{ option, step, errArr }: { option: any, step: number, errArr: any[] },
promiseNum: number,
start: number,
end: number,
uploadDataArr: any[],
finishApiFn: () => void
) => {
const len = errArr.length;
// 并发最多执行几次
const maxErrNum = Math.ceil(len / step);
const resumeArr: number[] = errArr.slice(start, end) || []; // 降序
let fnArr: any[] = [];
_.each(resumeArr, (item: any) => {
const i = Number(_.get(item, ['index']));
const partNumber = Number(_.get(item, ['PART_NUM']));
const params: any = preData(option, { ...item, PART_NUM: partNumber }, i);
const formData = getFormData(params, option);
const successItem: any = _.get(uploadDataArr, [0])
const p1 = {
KEY: _.get(successItem, 'KEY'),
ID: _.get(successItem, 'ID'),
NAME: _.get(successItem, 'NAME'),
PART_NUM: partNumber,
}
const urlParams = queryParams(p1);
const f1 = uploadFn(
{ step, urlParams, formData, headers, option, resumeFromBreakPointFlag: true },
i,
uploadDataArr,
finishApiFn
);
fnArr.push(f1);
});
let errPromiseFlag = false;
Promise.all(fnArr)
.then((res: any) => {
})
.catch(err => {
errPromiseFlag = true;
})
.finally(() => {
promiseNum++;
if (maxErrNum === promiseNum) {
finishApiFn && finishApiFn();
if (!errPromiseFlag) {
errPromiseFlag = _.some(uploadDataArr, item => !_.get(item, 'success'));
}
errPromiseFlag && showError('文件上传失败');
}
let start1 = end > len ? len - (end - start) : end;
let end1 = start1 + step >= len ? len : start1 + step;
// 结束下标必须比开始下标大,至少差值为1
const con1 = start1 !== start && start1 >= 0 && start1 <= len - 1 && end1 - start1 >= 1 && end1 <= len;
if (!con1) return;
fnArr = [];
return runContinueFn({ option, step, errArr }, promiseNum, start1, end1, uploadDataArr, finishApiFn);
})
}