文件上传:MultipartFile 与配置

HTTP 文件上传走 multipart/form-data 编码。Spring MVC 用 MultipartResolver 解析这种请求,控制器里用 MultipartFile 收文件,配置得当的话几行代码就能完成上传。

三步配置

1. 声明 MultipartResolver

DispatcherServlet 会按 Bean 名 multipartResolver 查找解析器。Servlet 3.0+ 容器用标准实现:

@Configuration
public class MultipartConfig {
    @Bean(name = "multipartResolver")
    public MultipartResolver multipartResolver() {
        return new StandardServletMultipartResolver();
    }
}

2. 给 Servlet 开启 multipart 支持

容器需要知道临时目录与大小上限。用 Java 注册 DispatcherServlet 时(第 40 章骨架),在初始化器里配置:

// AbstractAnnotationConfigDispatcherServletInitializer 子类
@Override
protected void customizeRegistration(ServletRegistration.Dynamic registration) {
    registration.setMultipartConfig(new MultipartConfigElement(
            "/tmp/upload",          // 临时目录
            5 * 1024 * 1024,        // 单文件最大 5MB
            10 * 1024 * 1024,       // 整个请求最大 10MB
            0));                    // 阈值:达到后落盘
}

web.xml 时代则写 <multipart-config><max-file-size>...</max-file-size></multipart-config>。上限超了会抛 MaxUploadSizeExceededException,可在全局异常里转成 400/413 提示。

3. 控制器接收文件

@RestController
public class UploadApi {
    @PostMapping("/api/upload")
    public String upload(@RequestParam("file") MultipartFile file) throws IOException {
        if (file.isEmpty()) {
            return "文件为空";
        }
        String original = file.getOriginalFilename(); // 原始文件名(可能含路径,注意清理)
        long size = file.getSize();                   // 字节数
        String type = file.getContentType();          // 如 image/png
        file.transferTo(Path.of("/data/files/", original)); // 保存(需防路径穿越)
        return "上传成功:" + original + "," + size + " 字节," + type;
    }
}

MultipartFile 常用方法:getBytes() 拿字节数组、getInputStream() 流式读取、transferTo(File/Path) 落盘。生产环境建议把文件存对象存储/OSS,数据库只存 URL。

一次传多个文件

同名表单字段传多份,或用不同名字收多个:

@PostMapping("/api/upload/many")
public String uploadMany(@RequestParam("files") MultipartFile[] files) {
    return "收到 " + files.length + " 个文件";
}

文件 + JSON 一起传:@RequestPart

表单里同时带 JSON 数据和文件时,用 @RequestPart 按 part 名绑定(JSON part 的 Content-Type 需是 application/json):

@PostMapping("/api/upload/meta")
public String uploadWithMeta(@RequestPart("meta") UserDTO dto,   // JSON part
                             @RequestPart("file") MultipartFile file) {
    return "用户 " + dto.getName() + " 上传了 " + file.getOriginalFilename();
}

直接使用 Servlet API

不想用 MultipartFile 也可以直接声明 jakarta.servlet.http.Part 参数,Spring 会自动注入;MultipartHttpServletRequest 则用于传统 Servlet 风格遍历。

常见坑

  • 忘记声明 multipartResolver 或容器未开启 multipart → 抛 MultipartException / 参数解析失败。
  • 大小上限没配 → 大文件直接被容器拒绝或内存溢出。
  • transferTo 直接用原始文件名 → 路径穿越与重名覆盖风险,建议服务端重命名(UUID)。
  • 前端必须把 Content-Type 设为 multipart/form-data 且带 boundary,否则不是 multipart 请求。

上传 = Resolver(解析器)+ Servlet multipart 配置(容量与临时目录)+ MultipartFile(收文件)。三件套齐了,上传就是「收字段」那么简单;别忘了服务端校验类型与大小、落盘改名这两道安全工序。

笔记加载中…