分片上传简单实现重点是Promise的运用

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);
        })
}

你可能感兴趣的:(文件下载或者文件上传,typescript,react,javascript,文件上传)