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

如何配置Spring实现Multipart文件直接流式写入磁盘而非内存存储?

解决Spring Web大文件上传内存占用问题

核心原因

Spring默认的Multipart解析器会在请求到达控制器前,根据配置的内存阈值决定将文件存入内存还是磁盘。若阈值设置过高或未强制磁盘存储,大文件会被提前加载到内存,导致内存占用飙升。

解决方案

1. 调整Multipart配置(关键步骤)

通过配置强制所有文件直接写入磁盘,避免内存存储,同时延迟解析请求直到需要访问文件时。

Spring Boot 配置(application.properties)

# 指定临时文件存储路径
spring.servlet.multipart.location=/tmp/uploads
# 设置单个文件最大大小(根据业务调整)
spring.servlet.multipart.max-file-size=10GB
# 设置整个请求最大大小
spring.servlet.multipart.max-request-size=10GB
# 内存阈值设为0,强制所有文件写入磁盘
spring.servlet.multipart.file-size-threshold=0
# 延迟解析Multipart请求,避免提前加载文件到内存
spring.servlet.multipart.resolve-lazily=true

# Tomcat容器补充配置(防止请求截断)
server.tomcat.max-swallow-size=-1
server.tomcat.max-http-post-size=-1

传统Spring MVC 配置

配置StandardServletMultipartResolver并开启延迟解析:

@Bean
fun multipartResolver(): StandardServletMultipartResolver {
    val resolver = StandardServletMultipartResolver()
    resolver.setResolveLazily(true)
    return resolver
}

同时在web.xml中配置Servlet的Multipart参数:

<servlet>
    <servlet-name>dispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <multipart-config>
        <!-- 临时文件存储路径 -->
        <location>/tmp/uploads</location>
        <!-- 单个文件最大大小(10GB) -->
        <max-file-size>10737418240</max-file-size>
        <!-- 整个请求最大大小 -->
        <max-request-size>10737418240</max-request-size>
        <!-- 强制所有文件写入磁盘 -->
        <file-size-threshold>0</file-size-threshold>
    </multipart-config>
    <load-on-startup>1</load-on-startup>
</servlet>

2. 优化控制器代码,流式处理文件

避免直接使用MultipartFile(虽然配置后它会从磁盘读取,但直接用InputStream更直观),通过流式读取写入目标位置,避免一次性加载文件到内存。

方式一:直接注入InputStream

@PostMapping(consumes = [MediaType.MULTIPART_FORM_DATA_VALUE])
fun insert(@RequestPart("file") fileStream: InputStream) {
    // 流式写入目标磁盘路径
    val targetPath = Paths.get("/path/to/your/target/directory/uploaded-file")
    Files.copy(fileStream, targetPath, StandardCopyOption.REPLACE_EXISTING)
    
    // 或者自定义分块处理(适合需要逐块处理的场景)
    /*val buffer = ByteArray(8192)
    var bytesRead: Int
    FileOutputStream(targetPath.toFile()).use { fos ->
        while (fileStream.read(buffer).also { bytesRead = it } != -1) {
            fos.write(buffer, 0, bytesRead)
        }
    }*/
}

方式二:使用MultipartHttpServletRequest

如果需要更多文件元数据(如文件名),可以通过MultipartHttpServletRequest获取文件信息并流式处理:

@PostMapping(consumes = [MediaType.MULTIPART_FORM_DATA_VALUE])
fun insert(request: HttpServletRequest) {
    if (request is MultipartHttpServletRequest) {
        val filePart = request.getFile("file") ?: return
        val fileName = filePart.originalFilename
        filePart.inputStream.use { inputStream ->
            val targetPath = Paths.get("/path/to/target/directory", fileName)
            Files.copy(inputStream, targetPath, StandardCopyOption.REPLACE_EXISTING)
        }
    }
}

验证效果

配置完成后,调试时会发现请求到达控制器前内存无明显飙升,文件会被逐步写入临时目录,再由控制器流式转存到目标位置,全程避免大文件占用内存。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 06:55:18