You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring+React图片上传遭遇415状态码问题的排查与解决

问题分析与解决思路

核心错误原因

Spring后端问题

  1. @RequestBody无法解析multipart请求:你用@RequestBody Assets assets接收实体,但@RequestBody是用来解析JSON/XML这类单一格式请求体的,在multipart/form-data请求里,普通表单字段和文件是分开的部分,没法直接用这个注解解析实体。
  2. consumes配置冲突:你同时指定了multipart/form-data和application/json,但实际发送的是multipart请求,Spring无法匹配正确的解析方式,导致415错误。

React前端问题

  1. 未正确使用FormData:你创建了FormData但没实际发送,反而自己拼接了包含asset和文件的对象,Axios会把这个对象序列化成JSON,完全不符合multipart请求格式。
  2. 手动设置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,提问作者Артём Орёл

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 19:07:05