You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Axios拦截器无法重试PUT方法文件上传问题求助

Axios PUT文件上传请求令牌过期后不重试的问题解决

问题原因

这并非Axios的预期行为,核心问题在于文件上传使用的FormData是可读流对象:当第一次请求发送后,流会被完全消耗,直接重试原请求时,FormData已经为空,导致重试请求无法正常携带文件数据。而普通JSON请求的data是普通对象,不存在流消耗的问题,所以能正常重试。

解决办法

1. 在拦截器中备份FormData,重试时恢复

在请求拦截器中提前备份FormData,响应拦截器重试时用备份数据重新构建请求:

// 请求拦截器
axios.interceptors.request.use(
  config => {
    // 备份FormData,避免流被消耗后无法重试
    if (config.data instanceof FormData) {
      const formDataCopy = new FormData();
      // 遍历原FormData的所有项,复制到备份中
      for (const [key, value] of config.data.entries()) {
        formDataCopy.append(key, value);
      }
      // 把备份存到请求配置的自定义字段中
      config._originalFormData = formDataCopy;
    }
    // 添加上当前令牌
    config.headers.Authorization = `Bearer ${localStorage.getItem('token')}`;
    return config;
  },
  error => Promise.reject(error)
);

// 响应拦截器
axios.interceptors.response.use(
  response => response,
  async error => {
    const originalRequest = error.config;
    // 仅处理401令牌过期且未重试过的请求
    if (error.response?.status === 401 && !originalRequest._retry) {
      originalRequest._retry = true;
      // 刷新令牌
      const refreshRes = await axios.post('/api/refresh-token');
      const newToken = refreshRes.data.token;
      localStorage.setItem('token', newToken);
      
      // 如果是FormData请求,恢复备份的表单数据
      if (originalRequest._originalFormData) {
        originalRequest.data = originalRequest._originalFormData;
      }
      // 更新请求头的令牌,重试请求
      originalRequest.headers.Authorization = `Bearer ${newToken}`;
      return axios(originalRequest);
    }
    return Promise.reject(error);
  }
);

2. 上传前主动刷新即将过期的令牌

如果不想处理拦截器的流备份逻辑,可以在发起文件上传前主动检查令牌有效期,提前刷新:

// 获取令牌过期时间(假设你的JWT包含exp字段,需要自行解析)
function getTokenExpiry() {
  const token = localStorage.getItem('token');
  if (!token) return 0;
  const payload = JSON.parse(atob(token.split('.')[1]));
  return payload.exp * 1000;
}

async function uploadFile(file) {
  const now = Date.now();
  const expiryTime = getTokenExpiry();
  // 如果令牌有效期不足5分钟,先刷新令牌
  if (expiryTime - now < 5 * 60 * 1000) {
    const refreshRes = await axios.post('/api/refresh-token');
    localStorage.setItem('token', refreshRes.data.token);
  }
  
  // 构建FormData并发起上传
  const formData = new FormData();
  formData.append('file', file);
  return axios.put('/api/upload', formData, {
    headers: { 'Content-Type': 'multipart/form-data' }
  });
}

注意事项

  • 确保刷新令牌的接口不需要携带过期的JWT,或者支持用刷新令牌(refresh token)来获取新的访问令牌,避免陷入循环。
  • 大文件上传场景下,提前刷新令牌的方案更稳妥,避免上传到一半因令牌过期中断后重试,节省带宽和时间。

内容的提问来源于stack exchange,提问作者Fersek

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 22:40:44