关于文件上传

我试图在接收 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 同时使用。
相关推荐
曲幽5 小时前
我用了FastApiAdmin后,连夜把踩过的坑都整理出来了
redis·python·postgresql·vue3·fastapi·web·sqlalchemy·admin·fastapiadmin
杜子不疼.5 小时前
【C++ AI 大模型接入 SDK】 - DeepSeek 模型接入(上)
开发语言·c++·chatgpt
加号35 小时前
【C#】 串口通信技术深度解析及实现
开发语言·c#
sycmancia6 小时前
Qt——编辑交互功能的实现
开发语言·qt
石山代码6 小时前
C++ 内存分区 堆区
java·开发语言·c++
前端若水6 小时前
会话管理:创建、切换、删除对话历史
前端·人工智能·python·react.js
无风听海6 小时前
C# 隐式转换深度解析
java·开发语言·c#
涛声依旧-底层原理研究所7 小时前
残差连接与层归一化通俗易懂的详解
人工智能·python·神经网络·transformer
一只大袋鼠7 小时前
Git 进阶(二):分支管理、暂存栈、远程仓库与多人协作
java·开发语言·git
csdn_aspnet7 小时前
Python 算法快闪 LeetCode 编号 70 - 爬楼梯
python·算法·leetcode·职场和发展