最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringMVC文件上傳請求問題分析

 更新時間:2024年07月02日 09:32:50   作者:lz-zxy  
這篇文章主要介紹了SpringMVC文件上傳請求,我們發(fā)的請求默認都是由DispatcherServlet類的doDispatch()來處理,這個方法的邏輯處理的第一步就是處理文件上傳的請求,我們一起來看看是怎么處理的吧

我們文件上傳接口只需要在方法參數(shù)上寫MultipartFile類,mvc就可以幫我們把上傳的文件封裝為這個類的對

象供我們非常方便的操作,那它是怎么做的呢?我們一起來看看

我們發(fā)的請求默認都是由DispatcherServlet類的doDispatch()來處理,這個方法的邏輯處理的第一步就是處理文件上傳的請求,我們一起來看看是怎么處理的吧。

本文分析的問題:文件上傳請求的執(zhí)行原理、文件上傳自動配置原理

執(zhí)行流程原理

checkMultipart()-處理文件上傳的請求

processedRequest = checkMultipart(request):處理文件上傳請求。所以我們把這個方法看明白就知道了

@Nullable
// 文件上傳解析器,只能有一個
private MultipartResolver multipartResolver;
protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
    // 1.利用文件上傳解析器來判斷是否是文件上傳請求
    if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
        // 如果之前被MultipartFilter包裝過了,就不做處理
        if (WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class) != null) {
            if (DispatcherType.REQUEST.equals(request.getDispatcherType())) {
                logger.trace("Request already resolved to MultipartHttpServletRequest, e.g. by MultipartFilter");
            }
        }
        // 是否有異常
        else if (hasMultipartException(request)) {
            logger.debug("Multipart resolution previously failed for current request - " +
                         "skipping re-resolution for undisturbed error rendering");
        }
        else {
            try {
                // 2、利用文件上傳解析器來解析文件上傳的請求
                return this.multipartResolver.resolveMultipart(request);
            }
            catch (MultipartException ex) {
                if (request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) != null) {
                    logger.debug("Multipart resolution failed for error dispatch", ex);
                    // Keep processing error dispatch with regular request handle below
                }
                else {
                    throw ex;
                }
            }
        }
    }
    // If not returned before: return original request.
    return request;
}

流程:

  • 利用 MultipartResolver(文件上傳解析器)來判斷是否是文件上傳請求:isMultipart

    默認使用StandardServletMultipartResolver

  • 利用 MultipartResolver(文件上傳解析器)來解析文件上傳的請求:resolveMultipart()

    會把請求包裝為StandardMultipartHttpServletRequest

這些工作都是利用文件上傳解析器來做的,所以我們把文件上傳解析器搞明白也就知道了

繼承圖:

MultipartResolver(文件上傳解析器)

主要作用就是把真實文件包裝為MultipartFile對象,并緩存起來以便后面封裝參數(shù)時使用

public interface MultipartResolver {
    // 判斷請求是否是文件上傳的請求
	boolean isMultipart(HttpServletRequest request);
    // 將請求包裝為 StandardMultipartHttpServletRequest
	MultipartHttpServletRequest resolveMultipart(HttpServletRequest request) throws MultipartException;
    // 清除資源
	void cleanupMultipart(MultipartHttpServletRequest request);
}

1、 isMultipart():判斷請求是否是文件上傳的請求。其實就是判斷 Content-Type 的值是否是以

multipart/form-data 或 multipart/ 開頭

(這里也就解釋了為啥我們發(fā)送文件上傳的請求時 Content-Type的值要為 multipart/form-data

2、resolveMultipart():將請求包裝為MultipartHttpServletRequest

MultipartResolver 的默認實現(xiàn)是 StandardServletMultipartResolver,它會把請求封裝為StandardMultipartHttpServletRequest,把文件封裝為StandardMultipartFile

// 是否延遲解析
private boolean resolveLazily = false;
// 判斷
public boolean isMultipart(HttpServletRequest request) {
    return StringUtils.startsWithIgnoreCase(request.getContentType(),
                                            (this.strictServletCompliance ? MediaType.MULTIPART_FORM_DATA_VALUE : "multipart/"));
}
// 包裝請求
public MultipartHttpServletRequest resolveMultipart(HttpServletRequest request) throws MultipartException {
    return new StandardMultipartHttpServletRequest(request, this.resolveLazily);
}

MultipartHttpServletRequest(文件上傳請求)

默認實現(xiàn)StandardMultipartHttpServletRequest,會把文件封裝為StandardMultipartFile

// 創(chuàng)建文件上傳請求
public StandardMultipartHttpServletRequest(HttpServletRequest request, boolean lazyParsing)
    throws MultipartException {
    super(request);
    // 是否延遲解析,默認false
    if (!lazyParsing) {
        // 解析請求
        parseRequest(request);
    }
}
private void parseRequest(HttpServletRequest request) {
    try {
        // 從 HttpServletRequest 中獲取上傳的文件
        Collection<Part> parts = request.getParts();
        this.multipartParameterNames = new LinkedHashSet<>(parts.size());
        // 存儲封裝好的文件對象
        MultiValueMap<String, MultipartFile> files = new LinkedMultiValueMap<>(parts.size());
        // 遍歷所有的文件
        for (Part part : parts) {
            String headerValue = part.getHeader(HttpHeaders.CONTENT_DISPOSITION);
            ContentDisposition disposition = ContentDisposition.parse(headerValue);
            // 獲取文件名字
            String filename = disposition.getFilename();
            if (filename != null) {
                // 添加到集合中
                files.add(part.getName(), new StandardMultipartFile(part, filename));
            }
            else {
                // 沒有文件名,就是普通參數(shù)了
                this.multipartParameterNames.add(part.getName());
            }
        }
        // 將上面所有生成的 StandardMultipartFile 文件對象設(shè)置到父類的 multipartFiles屬性中
        // 以便后面封裝參數(shù)時使用
        setMultipartFiles(files);
    }
    catch (Throwable ex) {
        handleParseFailure(ex);
    }
}
protected final void setMultipartFiles(MultiValueMap<String, MultipartFile> multipartFiles) {
    this.multipartFiles =
        new LinkedMultiValueMap<>(Collections.unmodifiableMap(multipartFiles));
}
  • 從 HttpServletRequest 中獲取上傳的文件 Part

  • 遍歷所有的文件

    Part 中獲取請求頭Content-Disposition的值,解析生成ContentDisposition對象,然后獲取文件名

    情況1:文件名不為空,說明是文件,把文件封裝為StandardMultipartFile對象

    情況2:文件名為空,說明是普通參數(shù),則保存參數(shù)名稱

  • 將上面所有生成的StandardMultipartFile文件對象設(shè)置到父類的multipartFiles屬性中,以便后面封裝參數(shù)時使用

整個執(zhí)行的原理到這里也就完畢了。

自動配置原理

文件上傳的自動配置類是MultipartAutoConfiguration

@AutoConfiguration
@ConditionalOnClass({ Servlet.class, StandardServletMultipartResolver.class, MultipartConfigElement.class })
@ConditionalOnProperty(prefix = "spring.servlet.multipart", name = "enabled", matchIfMissing = true)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(MultipartProperties.class)
public class MultipartAutoConfiguration {
	private final MultipartProperties multipartProperties;
	public MultipartAutoConfiguration(MultipartProperties multipartProperties) {
		this.multipartProperties = multipartProperties;
	}
	@Bean
	@ConditionalOnMissingBean(MultipartConfigElement.class)
	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;
	}
}
  • 啟用文件上傳的配置類MultipartProperties,配置前綴:spring.servlet.multipart

    也就是說我們可以通過這個類,然后在application.yml配置文件中來配置默認底層的規(guī)則

  • 給容器中導入了MultipartConfigElement類:文件上傳的配置

    我們可以在容器中自己注冊這個類

  • 給容器中導入了文件上傳解析器StandardServletMultipartResolver,標準的Servlet文件上傳解析器,用來處理文件上傳

    設(shè)置了resolveLazily屬性:解析文件是否延遲解析,默認不是延遲解析

我們也可以往容器中注冊我們自定義的文件上傳解析器,SpringBoot就會使用我們的。因為有條件注解來動態(tài)判斷

總結(jié)

執(zhí)行流程:利用 StandardServletMultipartResolver(文件上傳解析器)來包裝請求、把每一個真實文件封裝為MultipartFile類,以便我們能簡單的操作。還會把所有的MultipartFile對象設(shè)置到父類的multipartFiles屬性中,以便后面封裝參數(shù)時使用

自動配置:利用SpringBoot的自動配置往容器中注冊一個默認的文件上傳解析器

注意:文件上傳解析器默認全局只能有一個,不能像 HandlerMapping、HandlerAdapter 有多個

DispatcherServlet中就是這么寫的

@Nullable
	private MultipartResolver multipartResolver;
	private List<HandlerMapping> handlerMappings;
	/** List of HandlerAdapters used by this servlet. */
	@Nullable
	private List<HandlerAdapter> handlerAdapters;

到此這篇關(guān)于SpringMVC文件上傳請求的文章就介紹到這了,更多相關(guān)SpringMVC文件上傳請求內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • JAVA項目如何打包部署到Linux服務(wù)器上

    JAVA項目如何打包部署到Linux服務(wù)器上

    本文詳細介紹了在服務(wù)器上部署環(huán)境包括JDK、MySQL、Tomcat的設(shè)置,以及使用Idea-Maven-SpringBoot進行jar包打包部署的流程,內(nèi)容涵蓋了MySQL配置注意事項、pom.xml配置、打包命令等關(guān)鍵步驟,同時,也提供了如何將jar包上傳到Linux服務(wù)器并運行的具體方法
    2024-10-10
  • Springboot?hibernate-validator?6.x快速校驗示例代碼

    Springboot?hibernate-validator?6.x快速校驗示例代碼

    這篇文章主要介紹了Springboot?hibernate-validator?6.x校驗,本文以6.2.1.Final版本為例解決了log4j版本的漏洞問題,通過實例代碼給大家介紹的非常詳細,需要的朋友可以參考下
    2021-12-12
  • Java中的PrintWriter 介紹_動力節(jié)點Java學院整理

    Java中的PrintWriter 介紹_動力節(jié)點Java學院整理

    PrintWriter 是字符類型的打印輸出流,它繼承于Writer。接下來通過本文給大家介紹java中的 PrintWriter 相關(guān)知識,感興趣的朋友一起學習吧
    2017-05-05
  • Java AtomicInteger類使用方法實例講解

    Java AtomicInteger類使用方法實例講解

    這篇文章主要介紹了Java AtomicInteger類使用方法實例講解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-06-06
  • 為何Java單例模式我只推薦兩種

    為何Java單例模式我只推薦兩種

    這篇文章主要給大家介紹了關(guān)于Java單例模式推薦的兩種模式,文中通過示例代碼介紹的非常詳細,對大家學習或者使用Java具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧
    2019-06-06
  • Java數(shù)據(jù)結(jié)構(gòu)與算法實現(xiàn)遞歸與回溯

    Java數(shù)據(jù)結(jié)構(gòu)與算法實現(xiàn)遞歸與回溯

    本文主要介紹了Java數(shù)據(jù)結(jié)構(gòu)與算法實現(xiàn)遞歸與回溯,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • Java Web會話技術(shù)Session的簡單使用

    Java Web會話技術(shù)Session的簡單使用

    在請求需要傳遞的信息比較多,使用Cookie技術(shù)就會增大請求的難度。而Session可以存儲對象、數(shù)組等信息,并且Session是存儲到服務(wù)器端的,在客戶端請求時只需要將session id一并攜帶給服務(wù)器端。本文將簡單的介紹如何使用Session
    2021-05-05
  • SpringBoot 如何通過集成 Flink CDC 來實時追蹤 MySql 數(shù)據(jù)變動

    SpringBoot 如何通過集成 Flink CDC 來實時追蹤 M

    本文介紹如何在SpringBoot項目中集成FlinkCDC,實現(xiàn)對MySQL數(shù)據(jù)庫實時變動的捕獲與處理,包含環(huán)境準備、依賴引入、作業(yè)配置及測試驗證等步驟,幫助開發(fā)者構(gòu)建高效的數(shù)據(jù)同步與分析應(yīng)用,感興趣的朋友一起看看吧
    2025-06-06
  • dockerfile-maven-plugin極簡教程(推薦)

    dockerfile-maven-plugin極簡教程(推薦)

    這篇文章主要介紹了dockerfile-maven-plugin極簡教程,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-10-10
  • SpringBoot實現(xiàn)其他普通類調(diào)用Spring管理的Service,dao等bean

    SpringBoot實現(xiàn)其他普通類調(diào)用Spring管理的Service,dao等bean

    這篇文章主要介紹了SpringBoot實現(xiàn)其他普通類調(diào)用Spring管理的Service,dao等bean,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-11-11

最新評論

荔浦县| 曲水县| 益阳市| 昌邑市| 宜州市| 新绛县| 昌宁县| 黑水县| 万全县| 甘孜县| 隆德县| 渑池县| 县级市| 合肥市| 乌苏市| 卓尼县| 沾化县| 吉隆县| 沁水县| 兴安盟| 深泽县| 唐海县| 石台县| 扶风县| 合川市| 库伦旗| 澜沧| 环江| 泾源县| 武冈市| 从化市| 双鸭山市| 桃江县| 高安市| 白山市| 新乐市| 宾川县| 浏阳市| 宁河县| 巨野县| 金昌市|