SpringBoot上传相关配置

摘要:
我们可以清楚地知道,@ConditionalOnClass仅在加载特定类后使用。@ConditionalOnClass@ConditionalOnProperty使用@EnableConfigurationProperties启用自动配置文件。如果缺少配置文件,则默认为spring.http。multipart Enabled为true,这意味着默认情况下已启用。但似乎我们不知道我们使用什么上传方法,也不知道如何处理上传。然后继续往下看。

一、Spring boot上传自动配置的有哪些?

1、自动配置MultipartAutoConfiguration类

配置文件在Spring-boot-autoconfigorg.springframework.boot.autoconfigure.web包内

MultipartAutoConfiguration类我们先看下他的关系图

O ConditionalOnClass O ConditionalOnProperty O EnableConfigurationProperties O Configuration . MultipartAutoConfiguration

 springboot 的源代码如下:

@Configuration
@ConditionalOnClass({ Servlet.class, StandardServletMultipartResolver.class,
        MultipartConfigElement.class })
@ConditionalOnProperty(prefix = "spring.http.multipart", name = "enabled", matchIfMissing = true)
@EnableConfigurationProperties(MultipartProperties.class)
public class MultipartAutoConfiguration {
    private final MultipartProperties multipartProperties;
    public MultipartAutoConfiguration(MultipartProperties multipartProperties) {
        this.multipartProperties = multipartProperties;
    }
    @Bean
    @ConditionalOnMissingBean
    public MultipartConfigElement multipartConfigElement() {
        return this.multipartProperties.createMultipartConfig();
    }
    @Bean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
    @ConditionalOnMissingBean(MultipartResolver.class)
    public StandardServletMultipartResolver multipartResolver() {
        StandardServletMultipartResolver multipartResolver = new StandardServletMultipartResolver();
        multipartResolver.setResolveLazily(this.multipartProperties.isResolveLazily());
        return multipartResolver;
    }
}
从这开始我们来逐步分析一下。

我们那可以清楚的知道@ConditionalOnClass这类只是在特定的类加载后才使用。

@ConditionalOnClass({Servlet.class,StandardServletMultipartResolver.class,MultipartConfigElement.class})
@ConditionalOnProperty(prefix = "spring.http.multipart", name = "enabled", matchIfMissing = true)

    使用@EnableConfigurationProperties开启使用自动配置的文件,当配置文件缺失的时候默认 spring.http.multipart.enabled为true

这就是说明默认的时候启用。但是看到这里我们好像还不太清楚我们使用的是什么上传方式,而且上传的具体处理我们好像还不是太清楚。那就继续往下看。
我们可以看下MultipartProperties这个类 ,其实从定义上我们清楚了这是一个资源文件类。

二、上传的通用配置文件说明

代码如下:这里面配置的都是默认情况的配置文件,包括临时目录,文件最大大小,和最大的请求大小等
@ConfigurationProperties(prefix = "spring.http.multipart", ignoreUnknownFields = false)
public class MultipartProperties {
    /**
     * Enable support of multi-part uploads.
     * 默认使用multi-part的上传
     */
    private boolean enabled = true;
    /**
     * Intermediate location of uploaded files.
     */
    //上传的临时目录
    private String location;
    /**
     * Max file size. Values can use the suffixed "MB" or "KB" to indicate a Megabyte or
     * Kilobyte size.
     */
    private String maxFileSize = "1Mb";
    /**
     * Max request size. Values can use the suffixed "MB" or "KB" to indicate a Megabyte
     * or Kilobyte size.
     */
    private String maxRequestSize = "10Mb";
    /**
     * Threshold after which files will be written to disk. Values can use the suffixed
     * "MB" or "KB" to indicate a Megabyte or Kilobyte size.
     */
    private String fileSizeThreshold = "0";
    /**
     * Whether to resolve the multipart request lazily at the time of file or parameter
     * access.
     */
    private boolean resolveLazily = false;
    
    //省略setting和getting方法
    /**
     * Create a new {@link MultipartConfigElement} using the properties.
     * @return a new {@link MultipartConfigElement} configured using there properties
     */
    public MultipartConfigElement createMultipartConfig() {
        MultipartConfigFactory factory = new MultipartConfigFactory();
        if (StringUtils.hasText(this.fileSizeThreshold)) {
            factory.setFileSizeThreshold(this.fileSizeThreshold);
        }
        if (StringUtils.hasText(this.location)) {
            factory.setLocation(this.location);
        }
        if (StringUtils.hasText(this.maxRequestSize)) {
            factory.setMaxRequestSize(this.maxRequestSize);
        }
        if (StringUtils.hasText(this.maxFileSize)) {
            factory.setMaxFileSize(this.maxFileSize);
        }
        return factory.createMultipartConfig();
    }

 

重点来了:我们看下最后的一个创建 createMultipartConfig方法。这个方法很容易理解就是通过MultipartConfigFactory 来设置上面说的配置项。
 那我们在看下MultipartConfigFactory
其实我们看到源码的时候我们视乎感觉和上面的这个类差不多,唯一差别的就是最后一个的createMultipartConfig 返回的 MultipartConfigElement 对象。
这里我们就明白了这就是典型的工厂模式 来加载不用的工具而已。
/**
 * Factory that can be used to create a {@link MultipartConfigElement}. Size values can be
 * set using traditional {@literal long} values which are set in bytes or using more
 * readable {@literal String} variants that accept KB or MB suffixes, for example:
 * 就是处理传统配置的KB与MB的转换,其他的并没什么。主要是是最后都是kb
    
 * <pre class="code">
 * factory.setMaxFileSize(&quot;10Mb&quot;);
 * factory.setMaxRequestSize(&quot;100Kb&quot;);
 * </pre>
 *
 * @author Phillip Webb
 * @since 1.4.0
 */
public class MultipartConfigFactory {
    private String location;
    private long maxFileSize = -1;
    private long maxRequestSize = -1;
    private int fileSizeThreshold = 0;
    
    //省略 setting和getting
    /**
     * Create a new {@link MultipartConfigElement} instance.
     * @return the multipart config element
     */
    public MultipartConfigElement createMultipartConfig() {
        return new MultipartConfigElement(this.location, this.maxFileSize,
                this.maxRequestSize, this.fileSizeThreshold);
    }
}
MultipartConfigElement 就是最终的使用配置文件的类,再看源码我们看到就是有好几个构造方法,可以使用@MultipartConfig注解的方式等等。。。
讲到这里我们基本上清楚了 配置文件有了,这写配置文件也就是我们通用的上传配置。

三、配置文件加载以后,创建处理的组件。

对应自动配置类里的MultipartAutoConfiguration
1     @Bean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)//渲染的组件Bean
2     @ConditionalOnMissingBean(MultipartResolver.class)
3     public StandardServletMultipartResolver multipartResolver() {
4         StandardServletMultipartResolver multipartResolver = new StandardServletMultipartResolver();
5         multipartResolver.setResolveLazily(this.multipartProperties.isResolveLazily());
6         return multipartResolver;
7     }
这个方法的处理就是选择哪种Multipart 处理方式,默认情况下使用的是StandardServletMultipartResolver 。我们可以通过MultiparResolver来了解一下有哪些部件。
 

四、MultipartResolver介绍。

    在springMVC中框架底层实际上也是使用了通用的早期的 jsp smart upload 和 Oreilly 的 COS 类库,以及用最多的 Commons FileUpload 类库,实际开发中,我们只需要使用这些专门针对表单的文件上传处理类库即可。 
MultipartResolver 位于 HandlerMapping 之前,请求一来就交由它来处理。当 Web 请求到达 DispatcherServlet 并等待处理的时候,DispatcherServlet 首先会检查能否从自的 WebApplicationContext 中找到一个名称为 multipartResolver(由 DispatcherServlet 的常量 MULTIPART_RESOLVER_BEAN_NAME 所决定)的 MultipartResolver 实例。如果能够获得一个 MultipartResolver 的实例,DispatcherServlet 将调用 MultipartResolver 的 isMultipart(request) 方法检查当前 Web 请求是否为 multipart类型。如果是,DispatcherServlet 将调用 MultipartResolver 的 resolveMultipart(request) 方法,对原始 request 进行装饰,并返回一个 MultipartHttpServletRequest 供后继处理流程使用(最初的 HttpServletRequest 被偷梁换柱成了 MultipartHttpServletRequest),否则,直接返回最初的 HttpServletRequest。
 
需要特别说明的是MultipartResolver 只是一个接口,具体的实现SpringBoot给我们提供了CommonsMultipartResolver与StandardServletMultipartResolver两种处理方式(这里说的Springboot提供的处理方式,其实就是SpringMVC提供的)。
MultipartResolver 的 isMultipart(request) 方法好实现,当判断出当前的 request 是 multipart 类型的请求,它将调用 MultipartResolve 的 resolveMultipart(request)。这里的 request 就是原始的 HttpServletRequest 对象,奇迹就出现在这里。以 CommonsMultipartResolver 为例,当调用 resolveMultipart(request) 时,看看它是如何创建 MultipartRequest 的。
    @Override
    public boolean isMultipart(HttpServletRequest request) {
        return (request != null && ServletFileUpload.isMultipartContent(request));
    }
    @Override
    public MultipartHttpServletRequest resolveMultipart(final HttpServletRequest request) throws MultipartException {
        Assert.notNull(request, "Request must not be null");
        if (this.resolveLazily) {
            return new DefaultMultipartHttpServletRequest(request) {
                @Override
                protected void initializeMultipart() {
                    MultipartParsingResult parsingResult = parseRequest(request);
                    setMultipartFiles(parsingResult.getMultipartFiles());
                    setMultipartParameters(parsingResult.getMultipartParameters());
                    setMultipartParameterContentTypes(parsingResult.getMultipartParameterContentTypes());
                }
            };
        }
        else {
            MultipartParsingResult parsingResult = parseRequest(request);
            return new DefaultMultipartHttpServletRequest(request, parsingResult.getMultipartFiles(),
                    parsingResult.getMultipartParameters(), parsingResult.getMultipartParameterContentTypes());
        }
    }
      暂且不管 resolveLazily 为何意。假设 resolveLazily 为 false,我们看 else 的片段。由于是 CommonsMultipartResolver,它的 parseRequest 方法将从原始的 HttpServletRequest 中解析出文件,得到基于 Commons FileUpload API 的 FileItem 对象。Spring 在这里封装了一下,对于 MultipartResolver 而言,它看到的就是 MultipartFile。注意最后的 return,它将构建一个 DefaultMultipartHttpServletRequest,也就是 MultipartRequest。它将 MultipartFile 和 MultipartParameter 作为构造函数的参数传入,在这个构造函数里,有 setMultipartFiles 这句话。这个方法正是 AbstractMultipartHttpServletRequest 里的方法,这样,AbstractMultipartHttpServletRequest 的实例变量 multipartFiles 就有正规来源了吧,即解决了上面我们提到的疑问。然去实现 MultipartRequest 接口里的方法就是轻而易举的事了。 

五、CommonsMultipartResolver与StandardServletMultipartResolve介绍。

 

SpringBoot上传相关配置第2张

 

 
StandardServletMultipartResolve为默认的实现来处理上传,将一个request包装成了一个StandardMultipartHttpServletRequest,这个类会使用parseRequest方法解析http报文,将上传文件封装成StandardMultipartFile挨个存储到MultiValueMap<String, MultipartFile>类型的map中并关联到处理后的request。
 
StandardServletMultipartResolve是servlet3.0以后的容器才能使用。
 
 SpringBoot上传相关配置第3张
 
CommonsMultipartResolver不会强制要求设置临时文件路径。默认情况下,这个路径就是Servlet容器的临时目录,我们也可以使用uploadTempDir属性来设置临时位置。
 
 
 

 

 

免责声明:文章转载自《SpringBoot上传相关配置》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇js sleep 暂停基于WebGL/Threejs技术的BIM模型轻量化之图元合并下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

随便看看

QMap与QHash

Qt提供两个主要的关联容器类:QMap和QHash。QMap的K和T有一对方便的函数keys()和values(),它们在处理小数据集时显的特别有用。QMap重载了value,返回一个给定键多有值的QList列表。在内部,它们都依赖于QHash,且都像QHash一样对K的类型有相同的要求。...

java.net.URISyntaxException的解决办法

直接采用Stringurl=“http:count=1”;HttpGethttpget=新的HttpGet(url);HttpResponseresponse=client.execute(httpget);例如,“|”&amp;因此,不能直接使用String而不是URI来访问。然后我们可以使用URL生成URI的方法来解决这个问题。代码如下:URLu...

ABB机器人功能程序(FUNC)

功能程序的应用范围非常广泛。熟练的人员可以根据不同的需求创建相应的功能程序。函数程序的固定格式是FUNC,返回结束。在ABB的学习中,许多学生对功能程序几乎一无所知,即使他们真的在使用它。在学习ABB的过程中,我遇到了几个用例,所以我总结了它们以加深我的理解。...

Sql Server:创建用户并指定该用户只能看指定的视图,除此之外的都不让查看

--当前数据库创建角色execsp_Addrole“seeview”--创建了一个数据库角色,--添加了只允许访问指定视图的用户:execsp_Addlogin“login”、“password”、“default database name”execsp_ Addlogin“per”、“oa”不能在此处执行,execsp_Adduser“login nam...

Python-正则

,三:量词*重复0次或多次{0,}+重复一次或多次{1,}?重复0或1次{1,0}{n}重复n次{n}{n,}重复n次,或更多次{n,m}将n次重复到m次Escape:如果字符串中有特殊字符要匹配,请在常规字符和字符串前面添加r。如果特殊字符在字符组中,则它们是匹配的特殊字符,但为了记忆,匹配时会转义所有特殊字符。...

笔试题多线程

多线程是实现异步的主要方式之一,异步不等于多线程。NET有很多异步编程支持。例如,Begin***和End***方法在许多地方都可用,这是一种异步编程支持。它的一些内部程序是使用多线程的异步编程,而其他程序是使用硬件功能的异步编程。因为多线程访问不使用锁定机制,所以更新将丢失。...