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屬性來設置臨時位置。
 
 
 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM