如何配置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
相关产品推荐
相关产品推荐

