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

Spring Boot Rest Controller中处理多Multipart文件的方法

处理Spring Boot中Form-Data格式的主表单+子表单图片请求

问题场景

我正在开发一个包含主表单与子表单的应用:主表单存储产品详情,子表单存储产品颜色详情。前端提交的API请求为form-data格式,请求体结构示例如下:

{
  "raw_material_id": 1137,
  "extra_wip": "",
  "is_active": "1",
  "size_ratio_name_id": "45",
  "is_new": 0,
  "created_at": null,
  "mrp": 125,
  "collection": null,
  "hsn_code": "62113300",
  "is_sync": 0,
  "extra_fabric_programming": "",
  "sub_category_name": "INDOWESTERN SET",
  "updated_at": null,
  "category_name": "MENS",
  "is_update": 0,
  "name": "8642",
  "season": null,
  "comment": "",
  "id": 1924,
  "itm_no": "",
  "seasonID": 7,
  "collectionID": 3,
  "raw_material_colors": [
    {
      "color_id": 123,
      "color_name": "test",
      "item_image": (binary)
    },
    {
      "color_id": 456,
      "color_name": "test1",
      "item_image": (binary)
    }
  ]
}

Content-Type详情:Content-Disposition: form-data,有UI截图辅助理解。

以下是我尝试的代码:

@PutMapping( value="mainitem/{id}" , consumes = { MediaType.APPLICATION_JSON_VALUE, MediaType.MULTIPART_FORM_DATA_VALUE })
public ResponseEntity<?> user(HttpServletRequest request, @PathVariable Integer id,@RequestBody MainItem mainItem,
         @RequestPart MultipartFile files) {
    
    
    MainItem mainItem2 = mainItemrepo.findById(id).get();
    mainItem2.setId(mainItem.getId());
    mainItem2.setCollection(collectionRepo.collectionForMainItem(mainItem.getCollectionID()));
    mainItem2.setSeason(seasonRepo.seasonForMainItem(mainItem.getSeasonID()));
    mainItem2.setCategory_id(categoryrepo.categoryForMainItem(mainItem.getCategory_name()));
    mainItem2.setSub_category_id(subcategoryrepo.subCategoryForMainItem(mainItem.getSub_category_name()));
    mainItem2.setComment(mainItem.getComment());
    mainItem2.setCreated_at(mainItem.getCreated_at());
    mainItem2.setUpdated_at(mainItem.getUpdated_at());
    mainItem2.setExtra_fabric_programming(mainItem.getExtra_fabric_programming());
    mainItem2.setExtra_wip(mainItem.getExtra_wip());
    mainItem2.setHsn_code(mainItem.getHsn_code());
    mainItem2.setIs_active(mainItem.getIs_active());
    mainItem2.setIs_new(mainItem.getIs_new());
    mainItem2.setIs_sync(mainItem.getIs_sync());
    mainItem2.setIs_update(mainItem.getIs_update());
    mainItem2.setItm_no(mainItem.getItm_no());
    mainItem2.setMrp(mainItem.getMrp());
    mainItem2.setName(mainItem.getName());
    mainItem2.setRaw_material_id(mainItem.getRaw_material_id());
    mainItem2.setSize_ratio_name_id(mainItem.getSize_ratio_name_id());
    mainItemrepo.save(mainItem2);
    //Colors
    for(Map<String, Object> listMap:mainItem.getRaw_material_colors()) {
        System.out.println(listMap.get("color_id").toString());
        System.out.println(listMap.get("color_name").toString());
        System.out.println(listMap.get("item_image").toString());
    }
    

    return ResponseEntity.status(HttpStatus.OK)
            .body(new error(200, "Data Edited Successfully", "OK", request.getRequestURI()));

}

解决方案

核心问题分析

当前代码存在3个关键问题:

  1. form-data是多部分请求,不能同时用@RequestBody和@RequestPart解析
  2. 子表单中的item_image是MultipartFile,无法直接映射到Map<String, Object>类型
  3. 多图片请求未与颜色项一一对应

步骤1:调整实体类结构

创建颜色详情实体类,明确字段类型:

public class RawMaterialColor {
    private Integer colorId;
    private String colorName;
    private MultipartFile itemImage;
    
    // 生成getter、setter方法
}

修改MainItem类,将颜色列表字段改为强类型:

public class MainItem {
    // 保留原有所有字段...
    private List<RawMaterialColor> rawMaterialColors;
    
    // 生成getter、setter方法
}

步骤2:重构Controller方法

使用@RequestPart分别接收主表单JSON数据和多文件列表,确保颜色与图片一一对应:

@PutMapping(value = "mainitem/{id}", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<?> updateMainItem(HttpServletRequest request, 
                                        @PathVariable Integer id,
                                        @RequestPart("mainItem") MainItem mainItem,
                                        @RequestPart("itemImages") List<MultipartFile> itemImages) {
    // 1. 处理主表单数据更新
    MainItem existingItem = mainItemrepo.findById(id)
            .orElseThrow(() -> new RuntimeException("目标产品不存在"));
    
    // 复制更新字段(可使用BeanUtils.copyProperties简化代码)
    existingItem.setCollection(collectionRepo.collectionForMainItem(mainItem.getCollectionID()));
    existingItem.setSeason(seasonRepo.seasonForMainItem(mainItem.getSeasonID()));
    existingItem.setCategory_id(categoryrepo.categoryForMainItem(mainItem.getCategory_name()));
    existingItem.setSub_category_id(subcategoryrepo.subCategoryForMainItem(mainItem.getSub_category_name()));
    existingItem.setComment(mainItem.getComment());
    existingItem.setCreated_at(mainItem.getCreated_at());
    existingItem.setUpdated_at(mainItem.getUpdated_at());
    existingItem.setExtra_fabric_programming(mainItem.getExtra_fabric_programming());
    existingItem.setExtra_wip(mainItem.getExtra_wip());
    existingItem.setHsn_code(mainItem.getHsn_code());
    existingItem.setIs_active(mainItem.getIs_active());
    existingItem.setIs_new(mainItem.getIs_new());
    existingItem.setIs_sync(mainItem.getIs_sync());
    existingItem.setIs_update(mainItem.getIs_update());
    existingItem.setItm_no(mainItem.getItm_no());
    existingItem.setMrp(mainItem.getMrp());
    existingItem.setName(mainItem.getName());
    existingItem.setRaw_material_id(mainItem.getRaw_material_id());
    existingItem.setSize_ratio_name_id(mainItem.getSize_ratio_name_id());
    
    mainItemrepo.save(existingItem);
    
    // 2. 处理颜色与图片关联逻辑
    List<RawMaterialColor> colorList = mainItem.getRawMaterialColors();
    for (int i = 0; i < colorList.size(); i++) {
        RawMaterialColor color = colorList.get(i);
        MultipartFile image = itemImages.get(i);
        
        // 示例:打印信息,实际可替换为图片存储逻辑(本地/OSS等)
        System.out.println("颜色ID: " + color.getColorId());
        System.out.println("颜色名称: " + color.getColorName());
        System.out.println("图片文件名: " + image.getOriginalFilename());
    }
    
    return ResponseEntity.status(HttpStatus.OK)
            .body(new error(200, "Data Edited Successfully", "OK", request.getRequestURI()));
}

步骤3:前端请求适配

前端需要将主表单序列化为JSON Blob,与图片一起通过FormData提交,示例代码:

const formData = new FormData();
// 构造主表单数据
const mainItemData = {
  raw_material_id: 1137,
  is_active: "1",
  // 其他主表单字段...
  rawMaterialColors: [
    { colorId: 123, colorName: "test" },
    { colorId: 456, colorName: "test1" }
  ]
};
// 添加主表单JSON部分
formData.append('mainItem', new Blob([JSON.stringify(mainItemData)], { type: 'application/json' }));
// 添加对应图片
formData.append('itemImages', document.getElementById('img1').files[0]);
formData.append('itemImages', document.getElementById('img2').files[0]);

// 发送PUT请求
fetch(`/mainitem/${id}`, {
  method: 'PUT',
  body: formData
});

内容的提问来源于stack exchange,提问作者Bhavik Desai

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 01:05:23