React中如何通过event.target.result获取多输入按钮的文件数据
上传多个文件的两种实现方案
方案一:并行独立上传每个文件(复用现有函数)
你的现有createUploadFileChannel已经支持单个文件上传,最直接的方式是对三个文件分别调用该函数,在Saga中并行处理三个上传任务。每个文件会通过独立的FileReader获取event.target.result,互不干扰。
步骤1:在Saga中发起多文件上传任务
import { call, put, take, all } from 'redux-saga/effects'; import { createUploadFileChannel, END } from './your-file-upload-channel'; function* uploadAllFilesSaga(url, files, opt) { // 为每个文件创建独立的上传channel const imageChannel = yield call(createUploadFileChannel, url, files.imageFile, { ...opt, AttachmentType: 2 }); const letterChannel = yield call(createUploadFileChannel, url, files.letterFile, { ...opt, AttachmentType: 3 }); const cvChannel = yield call(createUploadFileChannel, url, files.cvFile, { ...opt, AttachmentType: 4 }); try { // 并行监听三个channel的上传状态与结果 yield all([ handleUploadChannel(imageChannel, 'image'), handleUploadChannel(letterChannel, 'letter'), handleUploadChannel(cvChannel, 'cv'), ]); yield put({ type: 'UPLOAD_ALL_SUCCESS' }); } catch (err) { yield put({ type: 'UPLOAD_ALL_FAILED', payload: err }); } } // 封装单个channel的通用处理逻辑 function* handleUploadChannel(channel, fileType) { while (true) { const action = yield take(channel); if (action.err) { throw new Error(`Upload ${fileType} failed: ${action.err.message}`); } else if (action.success) { console.log(`${fileType} upload success:`, action.data); return action.data; } else if (action.progress) { yield put({ type: `UPLOAD_${fileType.toUpperCase()}_PROGRESS`, payload: action.progress }); } } }
调用方式
// 从React Hook Form获取三个文件 const files = { imageFile: formValues.inputImage[0], letterFile: formValues.inputLetter[0], cvFile: formValues.inputCV[0], }; // 在组件中触发Saga动作 dispatch({ type: 'UPLOAD_ALL_FILES', payload: { url: '/your-upload-endpoint', files, opt: {} } });
方案二:一次性打包上传三个文件(修改现有函数)
如果后端支持批量接收附件,可以修改createUploadFileChannel,等待三个文件全部读取完成后,组装包含所有文件数据的请求体发送。
修改后的多文件上传Channel函数
import { eventChannel, END } from 'redux-saga'; import store from './your-store'; // 你的Redux Store实例 export const createMultiUploadFileChannel = (url, files, opt) => { return eventChannel((emitter) => { const xhr = new XMLHttpRequest(); // 为每个文件创建FileReader,收集读取Promise const fileReadTasks = Object.entries(files).map(([fileKey, file]) => { return new Promise((resolve) => { const reader = new FileReader(); reader.onloadend = (e) => { // 每个文件的event.target.result在这里获取 resolve({ AttachmentType: fileKey === 'imageFile' ? 2 : fileKey === 'letterFile' ? 3 : 4, Title: file.name, FileType: file.type, data: e.target.result, }); }; reader.readAsDataURL(file); }); }); const onProgress = (e) => { if (e.lengthComputable) { const progress = e.loaded / e.total; emitter({ progress }); } }; const onFailure = () => { emitter({ err: new Error('Upload failed') }); emitter(END); }; xhr.upload.addEventListener('progress', onProgress); xhr.upload.addEventListener('error', onFailure); xhr.upload.addEventListener('abort', onFailure); xhr.onreadystatechange = () => { const { readyState, status } = xhr; if (readyState === 4) { if (status === 200) { const resp = xhr.responseText; console.log('Multi upload response:', resp); emitter({ success: true, data: resp }); emitter(END); } else { onFailure(); } } }; // 等待所有文件读取完成后发送请求 Promise.all(fileReadTasks).then((attachments) => { const body = { ...opt, OpenWorkAttachments: attachments, }; xhr.open('POST', url, true); xhr.setRequestHeader('Content-Type', 'application/json'); xhr.setRequestHeader( 'Authorization', `Bearer ${store.getState().client.user.data[0].access_token}` ); xhr.send(JSON.stringify(body)); }).catch(onFailure); return () => { xhr.upload.removeEventListener('progress', onProgress); xhr.upload.removeEventListener('error', onFailure); xhr.upload.removeEventListener('abort', onFailure); xhr.onreadystatechange = null; xhr.abort(); }; }, buffers.sliding(2)); };
在Saga中使用修改后的函数
function* uploadMultiFilesSaga(url, files, opt) { const multiChannel = yield call(createMultiUploadFileChannel, url, files, opt); try { while (true) { const action = yield take(multiChannel); if (action.err) { throw action.err; } else if (action.success) { console.log('All files uploaded:', action.data); yield put({ type: 'MULTI_UPLOAD_SUCCESS', payload: action.data }); return; } else if (action.progress) { yield put({ type: 'MULTI_UPLOAD_PROGRESS', payload: action.progress }); } } } catch (err) { yield put({ type: 'MULTI_UPLOAD_FAILED', payload: err.message }); } }
核心说明
- 两种方案都通过每个文件对应独立的FileReader来获取
event.target.result,确保文件数据不会混淆。 - 方案一适合后端仅支持单个文件上传的场景,方案二适合后端支持批量上传的场景,可根据接口能力选择。
内容的提问来源于stack exchange,提问作者Lucas
相关产品推荐
相关产品推荐

