前言
昨晚8点左右,正准备下班走人,突然,产品小姐姐的在QQ上猛地抖动了我一下。产品小姐姐果然是无事不登三宝殿。线上出了问题!!!!!!!好几个版本没有变动多的文件导入突然不行了。客户催运营,运营催产品,产品催我这个小开发。哎,苦逼的程序员。
说明
本项目用的是 SpringBoot 2.x
问题重现
代码语言:javascript复制org.springframework.web.multipart.MultipartException: Failed to parse multipart servlet request; nested exception is java.io.IOException: The temporary upload location [/tmp/tomcat.46678487236333023.8030/work/Tomcat/localhost/extend] is not valid
我的脑门多了许多黑人问号??/tmp/tomcat.46678487236333023.8030/work/Tomcat/localhost/extend
这是个啥文件夹?上传文件为啥会操作这个文件夹?这个文件夹是啥时候创建的呢?
带着这一连串的问题,我开始了面向google的开发。根据The temporary upload location is not valid
关键字,搜索到如下结果:
在这里插入图片描述 说的是,这个文件夹没有,需要手动在tmp下创建该文件夹。然后,我就在线上用我的common用户,创建了这个文件夹,文件夹创建好之后,我接着尝试去上传Excel。紧接着又报了一个:
代码语言:javascript复制Failed to parse multipart servlet request; nested exception is java.io.IOException: org.apache.tomcat.util.http.fileupload.FileUploadBase$IOFileUploadException: Processing of multipart/form-data request failed. /tmp/tomcat.46678487236333023.8030/work/Tomcat/localhost/extend/upload_2062a9c2_2ecf_4176_9622_6ae54d0fe80b_00000003.tmp (权限不够)
权限不够,然后一看我这个文件夹的默认权限drwxrwxr-x
,而我们项目部署的时候用的是Tomcat用户,这个用户是没有权限在我新创建的文件夹下写临时文件。
而我小小的common用户又没有权限进行chmod。无奈只能请求我们的运维帮忙,用Tomcat用户在tmp下创建这个文件夹。创建好之后,我再一试就可以。看似这个问题已经完美的解决了。但是,我的疑问还是没有被解答。
解决疑问
- 上传文件为啥会操作这个文件呢?
/tmp/tomcat.46678487236333023.8030/work/Tomcat/localhost/extend
是啥时候创建的呢? 带着这几个疑问,我继续google。
第一个问题 上传文件为啥会操作这个文件呢?
SpringBoot的文件上传处理是基于Servlet实现的,Content-type是multipart/form-data, boundary="boundaryStr",在Servlet2.5及早期版本之前,文件上传需要借助commons-fileupload组件来实现。从Servlet 3.0规范之后,提供了对文件上传的原生支持,进一步简化了应用程序的实现。 以Tomcat为例,在文件上传之后会通过将数据写入临时文件,最终将文件实体传参到应用层,如下:
在这里插入图片描述 Tomcat实现了Servlet3.0规范,通过ApplicationPart对文件上传流实现封装,其中,DiskFileItem描述了上传文件实体,在请求解析时生成该对象,需要关注的是,DiskFileItem声明了一个临时文件,用于临时存储上传文件的内容,SpringMVC对上层的请求实体再次封装,最终构造为MultipartFile传递给应用程序。 临时文件 临时文件的路径定义:
代码语言:javascript复制{temp_dir}/upload_xx_xxx.tmp
temp_dir是临时目录,通过系统属性java.io.tmpdir
指定,默认值为;
在这里插入图片描述 第一个问题解决了,接着就是第二个问题,既然,上传需要用到这个文件夹,那么这个文件夹是啥时候生成的呢?
第二个问题 `{temp_dir}/upload_xx_xxx.tmp` 是啥时候创建的呢?
很显然,上传的时候没有生成文件夹,不然不会报is not valid
。那就只有可能是项目启动的时候生成的。
为了验证我的想法:我在开发服务器进行了下模拟,首先,把tmp下所有tomcat为前缀的文件夹都删除了。然后重启应用。重启后查看。
在这里插入图片描述
重启之后我发现/tmp
下新生成了tomcat.5195341930943680007.8030
这个文件夹。跟原来的tomcat.46678487236333023.8030
不一致。这么说每次项目启动之后就会生成一个新的tomcat.xxxx地址。为了验证我的猜想,我又把项目重启了一遍。再观察,果然如下,结果如下图所示:
在这里插入图片描述
解决问题
最终解决这个问题呢?一个保险的方法就是指定上传文件的临时文件夹。在SpringBoot下只需要如下配置:
代码语言:javascript复制spring:
servlet:
multipart:
#开启swagger
enabled: true
#最大上传文件大小
max-file-size: 5MB
#临时文件夹
location: /srv/www/extend
或者使用配置类,如下:
代码语言:javascript复制@Configuration
public static class FileConfig {
@Bean
public MultipartConfigElement multipartConfigElement() {
MultipartConfigFactory factory = new MultipartConfigFactory();
factory.setMaxFileSize(DataSize.parse("5MB"));
factory.setMaxRequestSize(DataSize.parse("5MB"));
factory.setLocation("/srv/www/extend");
return factory.createMultipartConfig();
}
}
linux 命令查缺补漏
在这里插入图片描述
chmod
我们先用 ll
查看文件,结果如下:
总用量 4
drwxr-xr-x 3 test test 4096 6月 11 15:06 work
PS: 用 ls -ld 文件夹名
例如:ls -ld work/
可以指定查看work文件夹的属性。结果跟用ll一样的。
drwxr-xr-x | 3 | test | test | 4096 | 6月 11 15:06 | work |
---|---|---|---|---|---|---|
用户权限 | 连接数 | 所有者 | 用户组 | 文件大小 | 修改日期 | 文件夹名 |
文件类型与权限
在这里插入图片描述
- r:表示用户可以查看该目录下的内容,即可以使用"ls"命令
- w:表示用户可以修改该目录下的内容,包括增加、删除、重命名等
- x: 表示用户可以进入该目录,既可以使用"cd"命令 我们不仅可以用rwx表示文件的权限,还可以同数字表示,具体如下:
- r:4
- w:2
- x:1
每种身份各自的三个权限(r、w、x)分数是需要累加的,例如当权限为
[-rwxrwxr-x]
分数则是: owner=rwx=4 2 1=7 group=rwx=4 2 1=7 others=r-x=4 1=5 所以,该文件的权限数字就是 775。
总结
一个线上问题,将自己的种种不足都暴露出来了。挺好的。
参考
补习系列(11)-springboot 文件上传原理