skip to content
Wentao Zhang

处理非文件上传时的 Multipart 请求解析错误

/ 3 min read

前因

在处理非文件上传的请求时,Spring抛出了一个关于Multipart请求解析的错误。

错误日志

Terminal window
Failed to parse multipart servlet request; nested exception is java.io.IOException: The temporary upload location [/tmp/tomcat.8656187483509135391.8020/work/Tomcat/localhost/ROOT] is not valid
org.springframework.web.multipart.MultipartException: Failed to parse multipart servlet request; nested exception is java.io.IOException: The temporary upload location [/tmp/tomcat.8656187483509135391.8020/work/Tomcat/localhost/ROOT] is not valid

这个错误在描述上来看是发生在文件上传接口,但是报错的接口是非上传文件接口,之前没有遇到过所以记录一下

出现原因

  • 请求头设置不正确,经过排查前端请求接口请求头Content-Type设置成了multipart/form-data,所以会出现非上传接口出现MultipartException
  • Servlet临时目录不存在Servlet接收上传文件时会在/tmp目录下创建临时目录提高性能,所以在服务启动的时候会创建tmp/tomcat.8656187483509135391.8020/work/Tomcat/localhost/ROOT临时目录,但是由于CentOS 会清除临时文件,所以才会出现这个报错

解决方法

1. 修改配置

  • 设置正确的请求头
  • 检查/tmp/tomcat.8656187483509135391.8020/work/Tomcat/localhost/ROOT是否正常,避免再出现这样的问题,手动配置指定路径
    spring:
    servlet:
    multipart:
    location: /path/to

2. 设置CentOS 清除临时目录忽略

在下面这个文件中增加一行配置

Terminal window
/usr/lib/tmpfiles.d/tmp.conf

增加内容

Terminal window
X /tmp/tomcat.*

以上两个方案选其一即可

另外收获

在查看Spring Boot 源码的时候看到下面这段代码👇

/**
* Create a new {@link MultipartConfigElement} using the properties.
* @return a new {@link MultipartConfigElement} configured using there properties
* */
public MultipartConfigElement createMultipartConfig() {
MultipartConfigFactory factory = new MultipartConfigFactory();
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
map.from(this.fileSizeThreshold).to(factory::setFileSizeThreshold);
map.from(this.location).whenHasText().to(factory::setLocation);
map.from(this.maxRequestSize).to(factory::setMaxRequestSize);
map.from(this.maxFileSize).to(factory::setMaxFileSize);
return factory.createMultipartConfig();
}

在生成写入MultipartConfigFactory属性时,PropertyMapper减少了很多if,可读性更强也更加优雅,在日常开发中实用性很强