springboot文件上传异常

SpringBoot文件上传异常之提示The temporary upload location xxx is not valid

1. 问题描述

springBoot搭建的应用,一直工作得好好的,突然发现上传文件失败,提示org.springframework.web.multipart.MultipartException: Failed to parse multipart servlet request; nested exception is java.io.IOException: The temporary upload location [/tmp/tomcat.6239989728636105816.19530/work/Tomcat/localhost/ROOT] is not valid目录非法,实际查看目录,结果还真没有,下面就这个问题的表现,分析下SpringBoot针对文件上传的处理过程

堆栈信息:

org.springframework.web.multipart.MultipartException: Failed to parse multipart servlet request; nested exception is java.io.IOException: The temporary upload location [/tmp/tomcat.6239989728636105816.19530/work/Tomcat/localhost/ROOT] is not valid
        at org.springframework.web.multipart.support.StandardMultipartHttpServletRequest.handleParseFailure(StandardMultipartHttpServletRequest.java:122)
        at org.springframework.web.multipart.support.StandardMultipartHttpServletRequest.parseRequest(StandardMultipartHttpServletRequest.java:113)
        at org.springframework.web.multipart.support.StandardMultipartHttpServletRequest.(StandardMultipartHttpServletRequest.java:86)
        at org.springframework.web.multipart.support.StandardServletMultipartResolver.resolveMultipart(StandardServletMultipartResolver.java:93)
        at org.springframework.web.servlet.DispatcherServlet.checkMultipart(DispatcherServlet.java:1128)
        at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:960)
        at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:925)
        at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:974)
        at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:877)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:661)
        at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:851)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
        at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)

先简单描述下上面的原因,上传的文件会缓存到本地磁盘,而缓存的路径就是上面的/tmp/tomcat.6239989728636105816.19530/work/Tomcat/localhost/ROOT,接着引入的疑问就是:

  1. 为什么上传的文件要缓存到本地
  2. 为什么临时目录会不存在
  3. 什么地方实现文件缓存

2. 解决办法

  1. 应用重启
  2. 增加服务配置,自定义baseDir
    server.tomcat.basedir=/tmp/tomcat
    
  3. 注入bean,手动配置临时目录
@Bean
MultipartConfigElement multipartConfigElement() {
   MultipartConfigFactory factory = new MultipartConfigFactory();
   factory.setLocation("/tmp/tomcat");
   return factory.createMultipartConfig();
}
  1. 配置不删除tmp目录下的tomcat
vim /usr/lib/tmpfiles.d/tmp.conf

# 添加一行
x /tmp/tomcat.*
  1. 流拷贝
    tomcat中实现流的拷贝代码如下,org.apache.tomcat.util.http.fileupload.util.Streams#copy(java.io.InputStream, java.io.OutputStream, boolean, byte[]) , 看下面的实现,直观影响就是写得真特么严谨
public static long copy(InputStream inputStream,
         OutputStream outputStream, boolean closeOutputStream,
         byte[] buffer)
 throws IOException {
 OutputStream out = outputStream;
 InputStream in = inputStream;
 try {
     long total = 0;
     for (;;) {
         int res = in.read(buffer);
         if (res == -1) {
             break;
         }
         if (res > 0) {
             total += res;
             if (out != null) {
                 out.write(buffer, 0, res);
             }
         }
     }
     if (out != null) {
         if (closeOutputStream) {
             out.close();
         } else {
             out.flush();
         }
         out = null;
     }
     in.close();
     in = null;
     return total;
 } finally {
     IOUtils.closeQuietly(in);
     if (closeOutputStream) {
         IOUtils.closeQuietly(out);
     }
 }
}

3.为什么目录最后不存在了呢???

原因在这:
springboot启动时会创建一个/tmp/tomcat.*/work/Tomcat/localhost/ROOT的临时目录作为文件上传的临时目录,但是该目录会在n天之后被系统自动清理掉,这个清理是由linux操作系统完成的,具体的配置如下 vim /usr/lib/tmpfiles.d/tmp.conf

#  This file is part of systemd.
#
#  systemd is free software; you can redistribute it and/or modify it
#  under the terms of the GNU Lesser General Public License as published by
#  the Free Software Foundation; either version 2.1 of the License, or
#  (at your option) any later version.

# See tmpfiles.d(5) for details

# Clear tmp directories separately, to make them easier to override
v /tmp 1777 root root 10d             #10天未操作会删除10天前的数据
v /var/tmp 1777 root root 30d

# Exclude namespace mountpoints created with PrivateTmp=yes
x /tmp/systemd-private-%b-*
X /tmp/systemd-private-%b-*/tmp
x /var/tmp/systemd-private-%b-*
X /var/tmp/systemd-private-%b-*/tmp

参考文档

你可能感兴趣的:(JAVA相关)