1. 为什么需要告别临时文件?
在传统的SpringBoot文件上传方案中,我们最常用的就是MultipartFile接口。这个方案看似简单易用,但实际上隐藏着一个性能杀手——临时文件。我曾经在一个电商项目中遇到过这样的问题:用户上传商品图片时,前端进度条明明显示已经完成,但后端接口却迟迟没有响应。经过排查发现,这是因为服务器正在将上传的文件写入临时目录。
临时文件的产生源于multipart/form-data的工作机制。当客户端通过表单上传文件时,服务器会先将整个文件内容保存到磁盘临时目录,然后才将文件流封装成MultipartFile对象交给业务代码处理。这个过程中有几个明显的痛点:
首先,文件需要被完整写入磁盘两次。第一次是作为临时文件存储,第二次才是业务代码将其转存到目标位置。如果使用云存储服务,这个时间还会更长,因为文件需要从应用服务器再次上传到云存储。
其次,临时文件会占用服务器宝贵的磁盘空间。在高并发上传场景下,可能瞬间就会耗尽磁盘空间。我曾经就遇到过因为临时文件堆积导致服务器崩溃的情况。
最后,前端显示的上传进度并不真实。进度条只反映文件从客户端到应用服务器的传输情况,而真正的文件处理才刚刚开始。这会给用户造成"明明已经上传完了,为什么还在处理"的困惑。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. MultipartFile的临时文件机制解析
2.1 Multipart请求的底层处理流程
要理解临时文件的问题,我们需要深入multipart/form-data的处理机制。当Tomcat接收到multipart请求时,会在org.apache.catalina.connector.Request的parseParts方法中进行处理:
java复制File location;
String locationStr = mce.getLocation();
if (locationStr == null || locationStr.length() == 0) {
location = ((File) context.getServletContext().getAttribute(
ServletContext.TEMPDIR));
} else {
location = new File(locationStr);
if (!location.isAbsolute()) {
location = new File(
(File) context.getServletContext().getAttribute(ServletContext.TEMPDIR),
locationStr).getAbsoluteFile();
}
}
这段代码决定了临时文件的存储位置。如果没有特别配置,就会使用Servlet容器默认的临时目录。这也是为什么我们经常在服务器的/tmp目录下看到一堆莫名其妙的文件。
2.2 MultipartFile接口的真相
虽然MultipartFile提供了getInputStream()方法,看起来像是在操作流,但实际上这个流是从临时文件读取的。查看Spring的StandardMultipartHttpServletRequest源码:
java复制@Override
public InputStream getInputStream() throws IOException {
return this.part.getInputStream();
}
这个part实际上是DiskFileItem的实例,从名字就能看出它是基于磁盘的。也就是说,当我们调用getInputStream()时,Spring只是帮我们打开了这个临时文件的输入流而已。
2.3 临时文件配置参数
SpringBoot提供了一些配置参数来控制临时文件的行为:
properties复制spring.servlet.mult
