关于文件上传

我试图在接收 JSON 格式请求的接口中,直接用 MultipartFile 类型接收文件(字段名 image),但 Jackson 无法将 JSON 数据反序列化为 MultipartFile 这个抽象类型,因为它没有可用于实例化的构造方法(比如无参构造),就会报InvalidDefinitionException错。

后面直接用String类型接收文件了打算,毕竟最后都要传到云上。那么估计是前端处理了。

下面是原因:

MultipartFile 是 Spring 用于处理表单文件上传multipart/form-data 格式)的接口,而不是处理 JSON 格式的类型,两者的使用场景完全冲突:

  1. JSON 格式 :通过 @RequestBody 接收,Jackson 会把 JSON 字符串解析成 Java 对象,但 MultipartFile 是抽象接口,没有默认构造方法,也无法被 Jackson 序列化 / 反序列化;
  2. 错误场景
    • 我的 CreateClothingRequest DTO 中包含 MultipartFile image 字段;
    • 接口用 @RequestBody 接收 JSON 请求,Spring 试图用 Jackson 解析 JSON 中的 image 字段为 MultipartFile,但解析失败;
    • 异常日志中 "abstract types either need to be mapped to concrete types" 也明确指出:MultipartFile 是抽象类型,无法直接实例化。

简单说:JSON 格式无法传输文件,MultipartFile 也不能被 JSON 解析,你混淆了 "JSON 请求" 和 "文件上传请求" 的处理方式

解决方案(正确的文件上传 + JSON 参数处理)

文件上传的请求格式必须是 multipart/form-data(而非 application/json),同时要调整 DTO 和接口的写法,以下是完整的正确实现:

步骤 1:修正 DTO (移除 MultipartFile,拆分参数)

如果你的请求既需要文件,又需要其他 JSON 格式的业务参数,有两种常见方案:

方案 1:表单方式(推荐,简单)

DTO 只保留非文件的业务字段,文件单独用 MultipartFile 接收:

复制代码
// CreateClothingRequest.java(仅保留业务字段)
@Data
public class CreateClothingRequest {
    private String name; // 服装名称
    private Integer type; // 服装类型
    private String style; // 风格标签
    // 其他业务字段...
    // 注意:移除 MultipartFile image 字段
}
方案 2:JSON 字符串 + 文件(适合复杂参数)

如果业务参数结构复杂,可将业务参数封装为 JSON 字符串,文件单独接收:

复制代码
// 无需修改 DTO(保留所有业务字段),接口层单独处理 JSON 字符串
步骤 2:修正接口写法(处理 multipart/form-data 格式)

接口不再用 @RequestBody,而是用 @RequestPart/@RequestParam 接收文件和参数:

方案 1 实现(表单方式)
复制代码
@RestController
@RequestMapping("/clothing")
public class ClothingController {

    // 关键:consumes 指定为 multipart/form-data,接收文件+表单参数
    @PostMapping(value = "/create", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public Result createClothing(
            // 接收文件(required = true 表示必传,可根据需求调整)
            @RequestPart("image") MultipartFile image,
            // 接收其他业务参数(如果参数多,也可以用 CreateClothingRequest 接收)
            @RequestParam("name") String name,
            @RequestParam("type") Integer type,
            @RequestParam("style") String style) {
        
        // 1. 处理文件上传(比如保存到本地/OSS)
        if (!image.isEmpty()) {
            String originalFilename = image.getOriginalFilename();
            // 示例:保存文件到本地
            image.transferTo(new File("/upload/" + originalFilename));
        }
        
        // 2. 处理业务参数,构建实体类
        CreateClothingRequest request = new CreateClothingRequest();
        request.setName(name);
        request.setType(type);
        request.setStyle(style);
        
        // 3. 执行业务逻辑
        // clothingService.create(request, image);
        
        return Result.success("创建服装成功");
    }
}
方案 2 实现(JSON 字符串 + 文件)

适合参数结构复杂的场景,业务参数以 JSON 字符串传递:

复制代码
@PostMapping(value = "/create", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public Result createClothing(
        @RequestPart("image") MultipartFile image,
        // 接收 JSON 字符串格式的业务参数
        @RequestPart("request") CreateClothingRequest request) {
    
    // 处理文件 + 业务参数
    // ...
    
    return Result.success();
}
步骤 3:客户端请求示例(PostMan / 前端)

请求格式必须是 multipart/form-data,而非 JSON:

  1. 请求头Content-Type: multipart/form-data(客户端会自动加,无需手动写);
  2. 请求体
    • 选择 form-data 格式;
    • 新增键值对:
      • 键:image,类型选 File,值选择要上传的文件;
      • 键:name,类型选 Text,值填 "短袖 T 恤";
      • 键:type,类型选 Text,值填 1;
      • 其他业务参数同理。

关键补充:为什么不能用 JSON 传文件?

JSON 是文本格式 ,只能传输字符串、数字、布尔等基础类型,无法直接传输二进制文件(比如图片、视频)。文件上传的标准方式是用 multipart/form-data 格式,它专门用于传输二进制数据 + 表单参数。

总结

  1. 核心原因 :混淆了 JSON 请求(application/json)和文件上传请求(multipart/form-data),试图用 Jackson 解析 MultipartFile 抽象类型导致失败;
  2. 关键修复
    • 接口改为接收 multipart/form-data 格式,移除 @RequestBody
    • DTO 中移除 MultipartFile 字段,文件单独用 @RequestPart 接收;
    • 客户端按 form-data 格式发送文件 + 业务参数;
  3. 避坑要点MultipartFile 仅用于 multipart/form-data 格式的文件上传,不能和 @RequestBody 同时使用。
相关推荐
小旭95272 小时前
Spring 纯注解配置与 SpringBoot 入门详解
java·开发语言·spring boot·后端·spring
暮冬-  Gentle°2 小时前
C++中的空对象模式变体
开发语言·c++·算法
二闹2 小时前
解锁Python的隐藏管家:with语句的原理与用法
后端·python·设计
m0_579393662 小时前
C++中的命令模式
开发语言·c++·算法
吴声子夜歌2 小时前
JavaScript——DOM与事件
开发语言·javascript·ecmascript
胡楚昊2 小时前
BUUCTF RE 看心情写(2)
开发语言·c#
干啥啥不行,秃头第一名2 小时前
实战:用Python开发一个简单的区块链
jvm·数据库·python
2401_879693872 小时前
C++跨平台开发实战
开发语言·c++·算法
皙然2 小时前
深度解析 “池化思想”:从设计模式到 Java 技术栈的落地与实践
java·开发语言·设计模式