React结合Material UI上传组件向API发送图片/文件失败的解决方案
问题描述
我使用Material UI的上传组件开发了一个图片上传组件。此前我仅使用HTML原生文件输入框实现过该功能,因此本次开发遇到了问题:文件可以正常选中并存入组件state,但当我将文件追加到FormData中发送到Node & Express服务端时,req.files始终返回空数组,服务端我使用multer解析上传的文件。
前端UI组件代码
const StepFive = ({ handlePrevStep, setListingImages, listingImages, createdListing, }) => { const created = useSelector((state) => state.listings.created); const [fileList, setFileList] = React.useState([]); const dispatch = useDispatch(); React.useEffect(() => { setListingImages(fileList); console.log(fileList); }, [fileList]); const onChange = ({ fileList: newFileList }) => { setFileList(newFileList); }; const handleLisitingImages = (e) => { e.preventDefault(); const data = new FormData(); // 校验是否选中了图片,将图片追加到FormData中 if (fileList) { for (let i = 0; i < fileList.length; i++) { data.append('listingImage', fileList[i]); } } console.log(data.getAll('listingImage')); uploadImagesToListing(createdListing._id, data)(dispatch); }; const onPreview = async (file) => { let src = file.url; if (!src) { src = await new Promise((resolve) => { const reader = new FileReader(); reader.readAsDataURL(file.originFileObj); reader.onload = () => resolve(reader.result); }); } const image = new Image(); image.src = src; const imgWindow = window.open(src); imgWindow.document.write(image.outerHTML); }; // const prevStep = (e) => { // e.preventDefault(); // handlePrevStep(); // }; // if (!created) return <Spinner />; return ( <Con> <Wrap> <Header> Step <b>5|5</b> </Header> <SubHeader>Pictures</SubHeader> <SubInfo> This is the most important part of creating a listing and attratcing guests. Really spend time on taking some good quality pictures of your place </SubInfo> <UploadForm enctype='multipart/form-data'> <ImgCrop rotate> <Upload listType='picture-card' fileList={fileList} onChange={onChange} onPreview={onPreview} name='image' > {fileList.length < 15 && '+ Upload'} </Upload> </ImgCrop> </UploadForm> <BtnCon> <Btn onClick={prevStep}>Prev</Btn> <Btn onClick={handleLisitingImages}>Finish</Btn> </BtnCon> </Wrap> </Con> ); };
上传请求封装代码
export const uploadImagesToListing = (listingId, images) => async (dispatch) => { await axiosInstance .post(`/listings/imageupload/${listingId}`, images, { headers: { 'Content-Type': `multipart/form-data;` }, }) .then((res) => { dispatch({ type: uploadedImages, }); }) .catch((err) => { console.log(err); }); };
服务端multer配置代码
const storage = multer.memoryStorage(); // 配置多文件上传,对应字段名为listingImage const multipleUpload = multer({ storage: storage }).array('listingImage'); router.post( '/imageupload/:listingId', verifyToken, multipleUpload, listings.imageUpload );
问题表现
在路由处理函数中打印req.files,始终返回空数组。
解决方案
Material UI Upload组件返回的fileList每一项是组件封装后的自定义对象,真实的原生File对象存储在originFileObj属性下。之前直接把封装后的对象追加到FormData中,multer无法识别为合法的文件对象,因此解析结果为空。
仅需要修改前端追加FormData的逻辑即可:
if (fileList) { for (let i = 0; i < fileList.length; i++) { // 取封装对象内的原生File对象传入 data.append('listingImage', fileList[i].originFileObj); } }
额外优化建议
使用axios发送FormData请求时,不需要手动设置Content-Type: multipart/form-data请求头,浏览器会自动生成带分隔符(boundary)的请求头,手动设置反而可能因为缺少boundary参数导致解析失败。
内容的提问来源于stack exchange,提问作者Stinny
相关产品推荐
相关产品推荐

