使用ReactJS与Axios更新图片时提交FormData请求失败的问题
React + Axios 携带FormData格式图片发起PUT请求失败问题排查
问题表现
- 实现带图片上传功能的患者信息更新PUT接口
- 已通过原生FormData API转换上传的图片文件,但只要把图片数据加入请求参数,接口就会触发报错
- 移除图片相关字段后,请求可正常调用后端API
原问题代码
表单提交方法
onSubmit = (e) => { e.preventDefault(); let fd = new FormData(); fd.append('image', this.state.images, this.state.images.name); console.log(fd); const { id, nom } = this.state.patient; axios.put('/patients/' + this.props.match.params.id, {id, nom, fd}) .then((result) => { this.props.history.push("/show/" + this.props.match.params.id) if (result.data != null){ this.setState(this.initialState); } }); }
图片选择处理方法
handleFile(e) { this.setState({ images: e.target.files[0] }); this.setState({ image: '' }); let reader = new FileReader(); let images = e.target.files[0]; reader.onloadend = () => { this.setState({ file: images, imagePreviewUrl: reader.result }); } reader.readAsDataURL(images) }
问题根因定位
- 请求体构造逻辑错误:原代码将FormData实例
fd作为普通属性嵌套在纯JS对象{id, nom, fd}中传给Axios,Axios默认会将传入的普通JS对象序列化为JSON字符串,嵌套在内的FormData实例完全不会被正确解析为multipart表单结构,后端无法识别文件数据就会触发请求错误。 - 请求头格式不匹配:默认请求会携带
application/json的Content-Type头,和FormData要求的multipart/form-data格式不匹配,即使后端接收到请求也无法正确解析表单内容。
修正方案
- 所有需要提交的字段(包括文本字段、文件字段)全部直接append到同一个FormData实例中,不要将FormData嵌套在普通JS对象里
- 将完整的FormData实例直接作为请求体传给Axios,Axios会自动生成带边界标识的正确
multipart/form-data请求头
修正后的提交方法示例:
onSubmit = (e) => { e.preventDefault(); let fd = new FormData(); const { id, nom } = this.state.patient; // 文本字段直接append到FormData中 fd.append('id', id); fd.append('nom', nom); // 仅当存在选中的图片文件时再append,避免空值导致的报错 if (this.state.images) { fd.append('image', this.state.images, this.state.images.name); } // 直接将FormData实例作为请求体传给Axios axios.put('/patients/' + this.props.match.params.id, fd) .then((result) => { this.props.history.push("/show/" + this.props.match.params.id) if (result.data != null){ this.setState(this.initialState); } }); }
常见踩坑提醒
- 不要手动在请求头中写死
Content-Type: multipart/form-data,浏览器和Axios会根据FormData的内容自动生成包含boundary参数的完整请求头,手动写死会导致boundary缺失,后端无法拆分解析各个表单字段。 - FormData实例不能通过直接console.log打印出内部存储的字段值,调试时需要用
fd.getAll('字段名')的方法查看实际存储的内容。 - 调试时可在浏览器开发者工具的网络面板中查看请求Payload,确认所有字段是否以表单形式展示,而非被序列化成JSON字符串结构。
内容的提问来源于stack exchange,提问作者Lili lillas
相关产品推荐
相关产品推荐

