zoukankan      html  css  js  c++  java
  • SpringBoot上传相关配置

    一、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介绍。

     

     

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

     

     

    知识碎片,重在整理,路很长,一步一个脚印,就好。
  • 相关阅读:
    Validation failed for one or more entities. See 'EntityValidationErrors' property for more details
    Visual Studio断点调试, 无法监视变量, 提示无法计算表达式
    ASP.NET MVC中MaxLength特性设置无效
    项目从.NET 4.5迁移到.NET 4.0遇到的问题
    发布网站时应该把debug设置false
    什么时候用var关键字
    扩展方法略好于帮助方法
    在基类构造器中调用虚方法需谨慎
    ASP.NET MVC中商品模块小样
    ASP.NET MVC中实现属性和属性值的组合,即笛卡尔乘积02, 在界面实现
  • 原文地址:https://www.cnblogs.com/lmk-sym/p/6529232.html
Copyright © 2011-2022 走看看