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
相关产品推荐
相关产品推荐

