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

Spring Boot 3.x.x是否支持流式上传?升级遇阻求解决方案

Spring Boot 3.x 大文件流式上传替代方案(原Apache Commons Multipart场景适配)

1. 利用StandardServletMultipartResolver原生支持流式处理

Spring Boot 3默认的StandardServletMultipartResolver并非不支持流式处理,只是不再依赖Apache Commons FileUpload。只要正确配置并使用,完全可以实现低内存占用的大文件上传:

关键配置(application.properties)

# 单个文件最大限制,根据业务调整
spring.servlet.multipart.max-file-size=100MB
# 请求总大小限制
spring.servlet.multipart.max-request-size=100MB
# 超过此阈值的文件会写入临时磁盘文件,避免占用内存(默认0,即全部放内存)
spring.servlet.multipart.file-size-threshold=1MB
# 自定义临时文件目录,避免系统临时目录的自动清理问题
spring.servlet.multipart.location=/tmp/spring-multipart

流式处理代码示例

在Controller中直接通过MultipartFile的getInputStream()获取流,逐段处理而非一次性加载到内存:

@PostMapping("/upload")
public ResponseEntity<String> uploadLargeFile(@RequestParam("file") MultipartFile file) throws IOException {
    try (InputStream inputStream = file.getInputStream()) {
        // 示例:逐字节读取并写入磁盘(实际可替换为业务处理逻辑)
        File destFile = new File("/data/uploads/" + file.getOriginalFilename());
        try (OutputStream outputStream = new FileOutputStream(destFile)) {
            byte[] buffer = new byte[4096];
            int bytesRead;
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, bytesRead);
            }
        }
        return ResponseEntity.ok("文件上传成功");
    } catch (Exception e) {
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("上传失败:" + e.getMessage());
    }
}

2. 直接处理原始请求流(完全自定义解析)

如果需要更底层的控制,可以跳过Spring的Multipart解析,直接处理HttpServletRequest的原始输入流,搭配Apache Commons FileUpload的流式API(可单独引入依赖,无需Spring自动配置):

引入依赖(pom.xml)

<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.5</version>
</dependency>

自定义流式解析代码

@PostMapping("/stream-upload")
public ResponseEntity<String> streamUpload(HttpServletRequest request) throws IOException {
    // 禁用Spring的Multipart解析,避免提前消耗流
    request.setAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE, new MultipartException("手动解析"));
    
    ServletFileUpload fileUpload = new ServletFileUpload();
    // 设置缓冲区大小,超过则写入临时文件
    fileUpload.setSizeThreshold(1024 * 1024);
    fileUpload.setRepository(new File("/tmp/commons-fileupload"));
    
    try (InputStream inputStream = request.getInputStream()) {
        FileItemIterator iterator = fileUpload.getItemIterator(request);
        while (iterator.hasNext()) {
            FileItemStream item = iterator.next();
            if (!item.isFormField()) {
                try (InputStream itemStream = item.openStream()) {
                    // 流式处理文件内容
                    processLargeFileStream(itemStream, item.getName());
                }
            }
        }
        return ResponseEntity.ok("流式上传完成");
    } catch (FileUploadException e) {
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("解析失败:" + e.getMessage());
    }
}

private void processLargeFileStream(InputStream stream, String filename) throws IOException {
    File dest = new File("/data/uploads/" + filename);
    try (OutputStream out = new FileOutputStream(dest)) {
        byte[] buf = new byte[8192];
        int len;
        while ((len = stream.read(buf)) != -1) {
            out.write(buf, 0, len);
        }
    }
}

3. 异步场景:切换到Spring WebFlux(推荐高并发大文件场景)

如果项目可以适配异步非阻塞模型,Spring Boot 3的WebFlux提供了更高效的大文件流式处理能力,内存占用极低:

WebFlux Controller示例

@RestController
public class ReactiveUploadController {

    @PostMapping("/reactive-upload")
    public Mono<ResponseEntity<String>> uploadLargeFile(@RequestPart("file") FilePart filePart) {
        Path destPath = Paths.get("/data/uploads/" + filePart.filename());
        return filePart.transferTo(destPath)
                .then(Mono.just(ResponseEntity.ok("异步上传成功")))
                .onErrorResume(e -> Mono.just(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                        .body("上传失败:" + e.getMessage())));
    }

    // 更细粒度的流式处理(比如分段处理)
    @PostMapping("/reactive-stream-upload")
    public Mono<ResponseEntity<String>> streamUpload(@RequestPart("file") FilePart filePart) {
        return filePart.content()
                .map(dataBuffer -> {
                    // 处理单个DataBuffer(非阻塞)
                    byte[] bytes = new byte[dataBuffer.readableByteCount()];
                    dataBuffer.read(bytes);
                    DataBufferUtils.release(dataBuffer);
                    // 此处可替换为业务逻辑,比如写入Kafka或处理字节段
                    return bytes;
                })
                .then(Mono.just(ResponseEntity.ok("流式处理完成")))
                .onErrorResume(e -> Mono.just(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                        .body("处理失败:" + e.getMessage())));
    }
}

4. 注意事项

  • 确保服务器磁盘有足够空间存放临时文件和上传后的文件
  • 生产环境建议配合分片上传(前端把大文件分成多个小片段上传,后端合并),避免单次请求超时或中断
  • 监控临时文件目录,定期清理未使用的临时文件

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 07:16:03