React Redux+Redux-Observable中Axios上传S3进度状态不更新问题
解决React Redux + Redux-Observable中Axios上传S3的进度状态不更新问题
我之前也踩过类似的坑!明明上传成功了,但进度条纹丝不动,确实挺闹心的。结合你用的技术栈,咱们从几个关键环节排查解决:
1. 确保Axios的onUploadProgress回调正确触发并dispatch action
首先,Axios的进度回调需要直接在请求配置里定义,而且要注意S3 PUT请求的核心特性——必须在请求头里指定Content-Length,否则Axios无法计算上传进度。另外,要确保回调里的action能正确被Redux捕获。
示例代码:
// 构建S3上传请求 const uploadFileToS3 = (file, signedUrl) => { const config = { headers: { 'Content-Length': file.size, // 关键:必须传入文件大小用于进度计算 'Content-Type': file.type, }, onUploadProgress: (progressEvent) => { const percentCompleted = Math.round( (progressEvent.loaded * 100) / progressEvent.total ); // 触发更新进度的Redux action store.dispatch({ type: 'UPLOAD_PROGRESS', payload: percentCompleted, }); }, }; return axios.put(signedUrl, file, config); };
2. 在Redux-Observable Epic中正确处理进度事件
如果你的上传逻辑是在Epic里管理的,要注意:onUploadProgress是请求过程中异步触发的,你需要把这些进度事件转化为Redux action,不能只处理请求的最终成功/失败结果。
可以通过创建Subject来监听进度回调,再和主请求流合并:
import { ofType } from 'redux-observable'; import { map, mergeMap, merge, from, catchError, of } from 'rxjs'; import { Subject } from 'rxjs'; import axios from 'axios'; const uploadEpic = (action$) => action$.pipe( ofType('START_UPLOAD'), mergeMap((action) => { const { file, signedUrl } = action.payload; // 创建Subject接收进度事件 const progressSubject = new Subject(); const uploadRequest = axios.put(signedUrl, file, { headers: { 'Content-Length': file.size, 'Content-Type': file.type, }, onUploadProgress: (event) => { const percent = Math.round((event.loaded * 100) / event.total); progressSubject.next({ type: 'UPLOAD_PROGRESS', payload: percent }); }, }); // 合并进度流和请求结果流 return merge( progressSubject, from(uploadRequest).pipe( map(() => ({ type: 'UPLOAD_SUCCESS' })), catchError((err) => of({ type: 'UPLOAD_FAILURE', payload: err })) ) ); }) );
3. 检查Upload Reducer是否正确处理进度action
最后,别漏了在reducer里添加对应分支更新progress状态:
const initialState = { progress: 0, isUploading: false, error: null, }; const uploadReducer = (state = initialState, action) => { switch (action.type) { case 'START_UPLOAD': return { ...state, isUploading: true, progress: 0 }; case 'UPLOAD_PROGRESS': return { ...state, progress: action.payload }; // 这里必须更新进度值! case 'UPLOAD_SUCCESS': return { ...state, isUploading: false, progress: 100 }; case 'UPLOAD_FAILURE': return { ...state, isUploading: false, error: action.payload }; default: return state; } };
额外注意点
- 如果你的上传用了S3分块上传(Multipart Upload),Axios的
onUploadProgress只会返回单块的进度,需要自己累加计算总进度;单文件PUT的话,上面的方法完全适用。 - 测试时尽量用大文件,小文件上传太快可能看不到进度变化,容易误以为回调没触发。
内容的提问来源于stack exchange,提问作者littlechad
相关产品推荐
相关产品推荐

