简单写一个支持进度和取消功能的上传方法

366 阅读3分钟

前言

上传文件是我们最常用的方法之一了,几乎每个项目里都会使用到,实现起来也并不复杂,今天我们就简单写一个上传方法,并且支持进度显示和取消上传功能。

方法实现

export const uploadFile = (file, options) => {
  const {
    url = VITE_GLOB_UPLOAD_URL,
    onProgress,
    showToastFn = showLoading,
    signal
  } = Object.assign({}, options);

  const hide = showToastFn && showToastFn();
  const formData = new FormData();
  formData.append('file', file);

  if (options && options.body) {
    Object.keys(options.body).forEach(key => {
      formData.append(key, options.body[key]);
    });
  }

  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open('POST', url);

    // 设置请求头
    xhr.setRequestHeader('Authorization', getToken());
    xhr.setRequestHeader('appCode', 'dsops');
    xhr.setRequestHeader('tenantId', tenantStore.getTenantVal);
    xhr.setRequestHeader('Org-Type', tenantStore.getOrgType);
    xhr.setRequestHeader('Org-Id', tenantStore.getOrgId);

    // 监听取消请求的信号
    if (signal) {
      signal.addEventListener('abort', () => {
        console.log('abort');
        xhr.abort();
      });
    }

    xhr.onload = () => {
      hide && hide(true);
      const response = JSON.parse(xhr.responseText);
      if (xhr.status === 200) {
        resolve(response.data);
      } else {
        reject(response);
      }
    };

    xhr.upload.onprogress = (event) => {
      if (event.lengthComputable) {
        const percentComplete = Math.floor((event.loaded / event.total) * 100);
        onProgress && onProgress(percentComplete);
      }
    };

    xhr.onerror = () => {
      hide && hide(false);
      reject(xhr.statusText);
    };

    xhr.onabort = () => {
      hide && hide(false);
      reject('请求已被取消');
    };

    xhr.send(formData);
  });
};

export const createUploadController = () => {
  const controller = new AbortController();
  return {
    signal: controller.signal,
    abort: () => controller.abort(),
  };
};

函数参数

  • file: 上传的文件对象,这是通过 <input type="file"> 或其他方式获取的文件。
  • options: 一个对象,包含一些可选配置,如上传的 URL、进度回调、信号控制、额外数据等。
    • url: 上传的目标 URL,默认为 VITE_GLOB_UPLOAD_URL
    • onProgress: 上传进度的回调函数。
    • showToastFn: 显示加载中的提示函数,默认使用 showLoading 函数。
    • signal: 用于控制上传请求取消的信号。

使用 Object.assign 合并默认配置和传入的 options 对象。

构建 FormData 对象

const formData = new FormData();
formData.append('file', file);

if (options && options.body) {
  Object.keys(options.body).forEach(key => {
    formData.append(key, options.body[key]);
  });
}

使用 FormData 对象来构建上传的数据,首先将文件附加到 formData,如果 options 中包含其他数据(body),则将其附加到 formData 中。

创建并发送 XMLHttpRequest

创建 XMLHttpRequest 对象

const xhr = new XMLHttpRequest();
xhr.open('POST', url);

创建 XMLHttpRequest 对象,并使用 POST 方法打开连接到指定的 url

设置请求头

xhr.setRequestHeader('Authorization', getToken());
xhr.setRequestHeader('appCode', 'dsops');
xhr.setRequestHeader('tenantId', tenantStore.getTenantVal);
xhr.setRequestHeader('Org-Type', tenantStore.getOrgType);
xhr.setRequestHeader('Org-Id', tenantStore.getOrgId);

通过 setRequestHeader 方法设置请求头,包括认证信息、应用代码、租户信息等。这些信息通常用于服务器端验证和处理请求。这些信息应该根据实际情况定义。

请求取消信号监听

if (signal) {
  signal.addEventListener('abort', () => {
    console.log('abort');
    xhr.abort();
  });
}

如果传入了 signal 参数,则为其添加一个监听器,在 signal 触发 abort 事件时,中止上传请求。

请求成功的回调

xhr.onload = () => {
  hide && hide(true);
  const response = JSON.parse(xhr.responseText);
  if (xhr.status === 200) {
    resolve(response.data);
  } else {
    reject(response);
  }
};

当上传完成时,onload 回调被触发。首先,隐藏加载提示,然后解析服务器返回的响应内容。

  • 状态码为200:表示请求成功,解析并返回上传结果。
  • 其他状态码:将服务器的响应作为错误处理。

上传进度的回调

xhr.upload.onprogress = (event) => {
  if (event.lengthComputable) {
    const percentComplete = Math.floor((event.loaded / event.total) * 100);
    onProgress && onProgress(percentComplete);
  }
};

当上传过程中触发 onprogress 事件时,计算并更新上传进度(以百分比表示)。如果提供了 onProgress 回调函数,则将进度百分比传递给该回调。

请求错误的回调

xhr.onerror = () => {
  hide && hide(false);
  reject(xhr.statusText);
};

如果请求出现错误(例如网络问题),触发 onerror 回调,隐藏加载提示,并返回错误信息。

请求被取消的回调

xhr.onabort = () => {
  hide && hide(false);
  reject('请求已被取消');
};

当请求被取消时,触发 onabort 回调,隐藏加载提示,并返回取消请求的错误信息。

发送请求

xhr.send(formData);

最后,将 formData 发送到服务器,开始上传文件。

createUploadController 函数

createUploadController 函数用于创建一个控制上传请求的对象。该对象包含两个属性:signalabort

export const createUploadController = () => {
  const controller = new AbortController();
  return {
    signal: controller.signal,
    abort: () => controller.abort(),
  };
};
  • signal: 用于监听和控制请求取消的信号。
  • abort: 取消上传请求的方法。当调用 abort 方法时,触发关联的 signalabort 事件,中止正在进行的上传。

总结

文件上传进度显示功能,是通过监听xhr的upload.onprogress事件实现的;中断文件上传是通过原生提供的AbortController对象实现的;其实都比较简单,主要是是否了解过相关接口,如果了解的话,还是十分简单的。