如何统一给上传和下载的请求提供进度显示?

请求使用的是 axios,可以通过 onUploadProgress 和 onDownloadProgress 进行进度获取,但是如果在 base 的 axios 实例上添加这些回调,这些进度数据不好在像 react 这些框架中渲染出来。

但是如果像这样使用,又需要在每次调用中添加进度的回调。

function rc() {
  useEffect(() => {
     const res = getSomeApi({ onUploadProgress: () => {}})
}, [])
}

一般这种情况应该怎么处理比较好

阅读 1.4k
2 个回答

下载文件

/**
 * 压缩文件并下载
 * @param opts
 * @param {string} opts.zipName - 压缩包名称
 * @param {Array<{ url: string; filename: string }>} opts.files - 文件列表
 * @param {() => void} [opts.onSuccess] - 下载成功的回调
 * @param {(v: number) => void} [opts.onProgress] - 下载进度的回调
 * @param {(error: Error) => void} [opts.onError] - 下载出错的回调
 */

import {zipSync} from 'fflate';
import {saveAs} from 'file-saver';

export const fetchAndZipFiles = async (opts: {
    zipName: string;
    files: Array<{url: string; filename: string}>;
    onSuccess?: () => void;
    onProgress?: (v: number) => void;
    onError?: (error: Error) => void;
}) => {
    const startTime = new Date().getTime();
    const {zipName, files, onProgress, onError, onSuccess} = opts;
    onProgress?.(5);

    const zipFiles: Record<string, Uint8Array> = {};
    const totalFiles = files.length;

    let downloadedFiles = 0;

    try {
        for (const file of files) {
            if (!file.filename) {
                throw new Error('File name is missing');
            }

            const response = await fetch(file.url);

            if (!response.ok) {
                throw new Error(`Failed to fetch ${file.url}: ${response.statusText}`);
            }

            const blob = await response.blob();
            const arrayBuffer = await blob.arrayBuffer();
            zipFiles[file.filename] = new Uint8Array(arrayBuffer);

            downloadedFiles += 1;
            // -- 更新进度以反映基于文件数量的总进度的90%
            onProgress?.(5 + Math.round((downloadedFiles / totalFiles) * 90));
        }

        setTimeout(() => {
            const zipData = zipSync(zipFiles);
            const zipBlob = new Blob([zipData], {type: 'application/zip'});

            saveAs(zipBlob, `${zipName}.zip`);

            // -- 保存文件后,将进度设置为100%
            onProgress?.(100);
            onSuccess?.();
        }, 500);
    } catch (error) {
        onError?.(error);
    }
};

上传文件

import axios from 'axios';

const onCustomRequest = async (data) => {

    const ossFormData = new FormData();
    ossFormData.append('file', data?.file);

    const service = axios.create({
        baseURL: __APP_ENV__,
        timeout: 1000 * 60,
        withCredentials: true,
    });

    service({
        method: 'POST',
        url: '',
        data: ossFormData,
        headers: {
            'Content-type': 'multipart/form-data',
            'Content-Disposition': 'attachment',
            Authorization: 'Bearer ' + localStorage.getItem('access_token'),
        },
        onUploadProgress: (progressEvent) => {
            const progressPercent = Number(
                ((progressEvent.loaded / progressEvent.total!) * 100).toFixed(2)
            );
            const percent = progressPercent >= 99 ? '99' : progressPercent.toString();
            //TODO
        },
    })
        .then((res) => {
            //TODO
        })
        .catch(() => {
            //TODO
        });
};
  1. 事件派发(发布订阅)
  2. 自定义 hook, 具体是抽离请求状态还是具体功能逻辑就看你自己需求。
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题