Spring Boot 文件上传接口,你会入参吗?

520 阅读4分钟

上期《编写接口的正确姿势》讲了SpringBoot接口入参及定义的规范,有朋友问“带有附件的接口应如何处理呢?”借着这个机会,我们一起再重温一下。 本期除了文件入参,还有 @ModelAttribute 的干货嗷

SpringBoot配置

在 Spring Boot 中处理文件上传,需先配置 Multipart 文件解析器。在 application.properties 中添加以下配置:

# 单个文件最大大小(默认1MB)
spring.servlet.multipart.max-file-size=10MB
# 单个请求最大总大小(默认10MB)
spring.servlet.multipart.max-request-size=50MB
# 是否延迟解析(大文件建议开启)
spring.servlet.multipart.resolve-lazily=true

如何写入参

单文件上传

使用 MultipartFile file 进行入参接收即可

@RestController
public class FileUploadController {

    @PostMapping("/upload")
    public String uploadFile(@RequestParam("file") MultipartFile file) {
        if (file.isEmpty()) {
            return "文件不能为空";
        }
        // 处理文件存储逻辑
        return "上传成功";
    }
}

多文件上传

使用 MultipartFile[] file 或者 List<MultipartFile> files 都可

@PostMapping("/multiUpload")
public String multiUpload(@RequestParam("files") MultipartFile[] files) {
    Arrays.stream(files).forEach(file -> {
        // 循环处理每个文件
    });
    return "批量上传成功";
}

与其它参数一起入参?

独立参数

filemeta 无逻辑关联时,我们可以使用 @RequestPart 进行文件和实体类的同时接收

@PostMapping(value = "/uploadWithMeta", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public String uploadWithMetadata(
    @RequestPart("file") MultipartFile file,
    @RequestPart("meta"@Valid FileMeta meta) {
    // 同时处理文件和元数据
    return "上传成功";
}

RequestPart 详解

注解适用场景示例
@RequestParam简单表单上传(键值对+文件)@RequestParam("file") MultipartFile file
@RequestPart复杂请求体(如混合JSON和文件)@RequestPart("metadata") MetaData data, @RequestPart("file") MultipartFile file

混合参数

filemeta 逻辑关联紧密时(例如自定义文件名称),可以通过带有 MultipartFile 的实体类进行入参

@Data
public class FileEntity {
    private String name;
    private MultipartFile multipartFile;
}

但这时候问题来了,使用 @RequestBody 接收参数,竟然报 multipartFilenull

@PostMapping("/upload")
public String uploadFile(@RequestBody FileEntity fileEntity) {
    String fileName = fileEntity.getName();
    MultipartFile file = fileEntity.getMultipartFile();
    // 处理业务逻辑
    return "上传成功";
}

经过验证,我发现省略 @RequestBody,直接使用缺省的 FileEntity 接收竟然成功了,这里就不得不引出 @ModelAttribute

public String uploadFile(FileEntity fileEntity)

@ModelAttribute 和 @RequestBody

  • @RequestBody:用于解析 JSON/XML 请求体,无法处理 multipart/form-data 格式,也就是我们熟悉的 application/json
  • @ModelAttribute:专为表单数据设计,支持混合普通字段与文件字段。

当我们需要 filemeta 混合接收时,前端传参格式应为 multipart/form-data,此时我们应使用 @ModelAttribute 进行入参接收,即可既拿到文件又拿到普通字段了。

那么问题又来了,我上面没有使用 @ModelAttribute,而是缺省了入参注解,为什么也能接受呢?

@ModelAttribute 缺省

在某些情况下,我们的入参注解是可以缺省的,以下举例:

  1. 传参格式为 multipart/form-data

    当请求体是 multipart/form-data 格式(即表单上传文件)时,Spring Boot 会自动将表单字段绑定到入参对象(如 FileEntity),此时可以省略 @ModelAttribute

    @PostMapping("/upload")
    public String uploadFile(FileEntity fileEntity) {  // 省略 @RequestBody
        return "上传成功: " + fileEntity.getName();
    }
    
  2. 传参格式为 query parameters

    对于 GET 请求,Spring Boot 会自动将查询参数绑定到方法入参对象(如 User),此时可以省略 @RequestBody

    @GetMapping("/user")
    public String getUser(User user) {  // 省略 @RequestBody
        return "用户信息: " + user.getName() + ", " + user.getAge();
    }
    

    前端传参: http://localhost:8080/user?name=John&age=25

  3. 传参格式为 application/json,不可缺省

    对于 application/json 格式的请求,Spring Boot 不会自动将请求体绑定到方法入参对象,必须显式使用 @RequestBody

    @PostMapping("/createUser")
    public String createUser(@RequestBody User user) {  // 必须使用 @RequestBody
        return "创建用户: " + user.getName();
    }
    
  4. 传参格式为 application/xml,不可缺省

    对于 application/xml 格式的请求,Spring Boot 不会自动将请求体绑定到方法入参对象,必须显式使用 @RequestBody

即使可以省略 @RequestBody,也建议显式添加注解以提高代码可读性,不要像笔者一样有缺省的坏习惯~

所以我在 "混合参数" 时缺省了注解,也依然是使用了 @ModelAttribute 进行了实体类的接收,此时是可以正常完成接收的。

总结

本次我们聊了聊 Spring Boot 在写文件上传接口时,应怎样更好地入参,那么对于文件上传,安全策略、文件校验、分片上传也是我们应该掌握的,下期我们再聊吧~

好看记得关注嗷~