Spring+React图片上传遭遇415状态码问题的排查与解决
问题分析与解决思路
核心错误原因
Spring后端问题
- @RequestBody无法解析multipart请求:你用
@RequestBody Assets assets接收实体,但@RequestBody是用来解析JSON/XML这类单一格式请求体的,在multipart/form-data请求里,普通表单字段和文件是分开的部分,没法直接用这个注解解析实体。 - consumes配置冲突:你同时指定了
multipart/form-data和application/json,但实际发送的是multipart请求,Spring无法匹配正确的解析方式,导致415错误。
React前端问题
- 未正确使用FormData:你创建了FormData但没实际发送,反而自己拼接了包含asset和文件的对象,Axios会把这个对象序列化成JSON,完全不符合multipart请求格式。
- 手动设置Content-Type错误:手动指定
multipart/form-data会丢失浏览器自动生成的boundary(分隔符),Spring无法识别请求的各个部分,导致解析失败。
具体修复步骤
1. Spring后端修复(两种可选方案)
方案一:用@RequestParam接收字段,手动构建Assets对象
适合字段较少的场景,直接把Assets的每个字段作为表单参数接收:
// 修改接口方法 @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public ResponseEntity<Object> saveAsset( @RequestParam String assettype, @RequestParam String assetBrand, @RequestParam String assetModel, @RequestParam(required = false) MultipartFile file1, @RequestParam(required = false) MultipartFile file2, @RequestParam(required = false) MultipartFile file3) throws IOException { // 手动构建Assets对象 Assets assets = new Assets(); assets.setAssettype(assettype); assets.setAssetBrand(assetBrand); assets.setAssetModel(assetModel); // 处理图片逻辑不变 if(file1 != null && file1.getSize() != 0){ Image image1 = toImageEntity(file1); image1.setPreviewImage(true); assets.addImageToItemName(image1); } if(file2 != null && file2.getSize() != 0){ Image image2 = toImageEntity(file2); assets.addImageToItemName(image2); } if(file3 != null && file3.getSize() != 0){ Image image3 = toImageEntity(file3); assets.addImageToItemName(image3); } Assets itemFromDB = assetsRepository.save(assets); itemFromDB.setPreviewImageId(itemFromDB.getImages().get(0).getId()); assetsRepository.save(itemFromDB); // 注意保存更新后的对象,否则预览图ID不生效 return ResponseEntity.ok().build(); }
方案二:用@RequestPart分别接收JSON实体和文件(更规范)
适合字段较多的场景,前端把Assets转成JSON字符串放在FormData里,后端用@RequestPart接收:
// 修改接口方法 @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public ResponseEntity<Object> saveAsset( @RequestPart("asset") Assets assets, @RequestPart(required = false) MultipartFile file1, @RequestPart(required = false) MultipartFile file2, @RequestPart(required = false) MultipartFile file3) throws IOException { // 处理图片逻辑不变 if(file1 != null && file1.getSize() != 0){ Image image1 = toImageEntity(file1); image1.setPreviewImage(true); assets.addImageToItemName(image1); } if(file2 != null && file2.getSize() != 0){ Image image2 = toImageEntity(file2); assets.addImageToItemName(image2); } if(file3 != null && file3.getSize() != 0){ Image image3 = toImageEntity(file3); assets.addImageToItemName(image3); } Assets itemFromDB = assetsRepository.save(assets); itemFromDB.setPreviewImageId(itemFromDB.getImages().get(0).getId()); assetsRepository.save(itemFromDB); return ResponseEntity.ok().build(); }
2. React前端修复
对应方案一的前端代码(直接把字段加入FormData)
const saveAsset = (event) => { event.preventDefault(); const formData = new FormData(); // 把Assets的字段逐个加入FormData formData.append('assettype', assettype); formData.append('assetBrand', assetBrand); formData.append('assetModel', assetModel); // 加入文件,判断文件是否存在避免空值 if (image1) formData.append('file1', image1); if (image2) formData.append('file2', image2); if (image3) formData.append('file3', image3); axios.post('http://localhost:8082/api/assets', formData) .then(res => { console.log(res.status); console.log(res.data); }) .catch(err => { console.error(err); }); };
对应方案二的前端代码(把Assets转成JSON字符串加入FormData)
const saveAsset = (event) => { event.preventDefault(); const formData = new FormData(); // 把Assets对象转成JSON字符串,作为单独的part加入 formData.append('asset', new Blob([JSON.stringify({ assettype, assetBrand, assetModel })], { type: 'application/json' })); // 加入文件 if (image1) formData.append('file1', image1); if (image2) formData.append('file2', image2); if (image3) formData.append('file3', image3); axios.post('http://localhost:8082/api/assets', formData) .then(res => { console.log(res.status); console.log(res.data); }) .catch(err => { console.error(err); }); };
关键注意事项
- 不要手动设置
Content-Type: multipart/form-data,Axios会自动处理并添加正确的boundary。 - 文件参数加上
required = false,避免未上传文件时请求报错。 - Spring端保存数据库时,必须保存更新后的
itemFromDB,否则预览图ID不会写入数据库。
内容的提问来源于stack exchange,提问作者Артём Орёл
相关产品推荐
相关产品推荐

