国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 開發(fā)設(shè)計(jì) > 正文

深入springMVC------文件上傳源碼解析(上篇)

2019-11-06 06:06:15
字體:
供稿:網(wǎng)友

最近在項(xiàng)目中,使用sPRingmvc 進(jìn)行上傳文件時(shí),出現(xiàn)了一個(gè)問題:

org.springframework.web.multipart.MultipartException: The current request is not a multipart request

....

以上堆棧信息省略。

乍看一下,沒啥值得討論的地方,就是說當(dāng)前這個(gè)請(qǐng)求不是一個(gè)multipart request,也就是說不是上傳文件的請(qǐng)求。但是,這結(jié)果還是令我稍感意外,為什么呢?因?yàn)椋冶疽馐菍⑽募@個(gè)參數(shù)作為非必要參數(shù),類似下面這樣:

@RequestMapping(value = "/upload", method = RequestMethod.POST)public ResultView upload(@RequestParam(value = "file", required = false) MultipartFile file)

spring拋出上面的異常,就違背了我的本意,我明明設(shè)置了 “required = false”, 為什么還是不行? 于是,帶著疑問去看了一下spring的源碼,下面就跟大家分享一下spring mvc對(duì)于文件上傳的處理。

 

--------------------------------------------------我是華麗的分割線-------------------------------------------------------

 

在spring mvc通過DispatcherServlet處理請(qǐng)求時(shí),會(huì)調(diào)用到 doDispatch這個(gè)方法,當(dāng)然這也是spring mvc處理請(qǐng)求最核心的方法:

復(fù)制代碼
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {        HttpServletRequest processedRequest = request;        HandlerExecutionChain mappedHandler = null;        boolean multipartRequestParsed = false;                WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);        try {            ModelAndView mv = null;            Exception dispatchException = null;            try {                processedRequest = checkMultipart(request);                multipartRequestParsed = (processedRequest != request);復(fù)制代碼

上面就是給出的有關(guān)上傳文件的代碼片段,看以看到,當(dāng)spring處理請(qǐng)求的時(shí)候,首先第一步就去檢查當(dāng)前請(qǐng)求是否為上傳文件的請(qǐng)求,那么,它是怎么檢查的呢,接著往下看:

復(fù)制代碼
protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {        if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {            if (WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class) != null) {                logger.debug("Request is already a MultipartHttpServletRequest - if not in a forward, " +                        "this typically results from an additional MultipartFilter in web.xml");            }            else if (request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) instanceof MultipartException) {                logger.debug("Multipart resolution failed for current request before - " +                        "skipping re-resolution for undisturbed error rendering");            }            else {                return this.multipartResolver.resolveMultipart(request);            }        }        // If not returned before: return original request.        return request;    }復(fù)制代碼

通過以上方法,我們可以看到如下邏輯:

(1)當(dāng) MultipartResolver 不為null的時(shí)候, 就通過它去檢查當(dāng)前請(qǐng)求是否為文件上傳請(qǐng)求(通過CommonsMultipartResolver的isMultipart方法)。

(2)如果當(dāng)前請(qǐng)求不是MultipartHttpServletReques并且不包含MultipartException異常,那么,就通過CommonsMultipartResolver去處理當(dāng)前請(qǐng)求(通過調(diào)用resolveMultipart方法將當(dāng)前請(qǐng)求包裝為MultipartHttpServletRequest),返回包裝后的請(qǐng)求。

(3)返回當(dāng)前請(qǐng)求(未經(jīng)處理的請(qǐng)求)。

接下來我們重點(diǎn)看看,spring是如何判斷是否為文件上傳的請(qǐng)求的:

CommonsMultipartResolver:

@Override    public boolean isMultipart(HttpServletRequest request) {        return (request != null && ServletFileUpload.isMultipartContent(request));    }

這兒直接使用了Apache 的commons-fileupload中的ServletFileUpload, 那我們就來看看它究竟何許人也:

ServletFileUpload:

復(fù)制代碼
public static final boolean isMultipartContent(            HttpServletRequest request) {        if (!POST_METHOD.equalsIgnoreCase(request.getMethod())) {            return false;        }        return FileUploadBase.isMultipartContent(new ServletRequestContext(request));    }復(fù)制代碼

以上代碼說明:

(1)當(dāng)前請(qǐng)求必須是post方法。

(2)如果是post方法,就通過 FileUploadBase 去進(jìn)一步檢測(cè)。

FileUploadBase:

復(fù)制代碼
public static final boolean isMultipartContent(RequestContext ctx) {        String contentType = ctx.getContentType();        if (contentType == null) {            return false;        }        if (contentType.toLowerCase(Locale.ENGLISH).startsWith(MULTIPART)) {            return true;        }        return false;    }復(fù)制代碼

以上方法說明:

只有當(dāng)當(dāng)前請(qǐng)求的contentType是 "multipart/" 的時(shí)候,才會(huì)將此請(qǐng)求當(dāng)做文件上傳的請(qǐng)求。

 

總結(jié):

綜合來看,spring其實(shí)是通過Apache的 commons-fileupload來檢測(cè)請(qǐng)求是否為文件上傳的請(qǐng)求。而commons-fileupload又是通過如下兩個(gè)條件來判斷:

1. 請(qǐng)求方法必須是 post.

2. 請(qǐng)求的contentType 必須設(shè)置為以 "multipart/" 開頭。

這下你該明白為什么我們?cè)谏蟼魑募臅r(shí)候必須要做的那些設(shè)置了吧。

 

好啦,回到文章開始的問題:

org.springframework.web.multipart.MultipartException: The current request is not a multipart request

這個(gè)問題是怎么導(dǎo)致的呢?

其實(shí)springmvc 在處理方法入?yún)⒌臅r(shí)候,發(fā)現(xiàn)了你的一個(gè)參數(shù)為 MultipartFile 類型或者是其數(shù)組或者包含他的容器類型,那么springmvc 就會(huì)通過上面類似的方法去檢驗(yàn)(通過contentType)。代碼如下:

復(fù)制代碼
private void assertIsMultipartRequest(HttpServletRequest request) {        String contentType = request.getContentType();        if (contentType == null || !contentType.toLowerCase().startsWith("multipart/")) {            throw new MultipartException("The current request is not a multipart request");        }    }復(fù)制代碼

那么這個(gè)問題該如何解決呢?

(1)ContentType 必須設(shè)置為 multipart/ 開頭的(通常是multipart/form-data)。我之所以會(huì)遇到這個(gè)問題,其實(shí)是因?yàn)樵贏PP請(qǐng)求的時(shí)候明明使用的multipart/form-data,但是卻始終通不過,嘗試用瀏覽器OK。

(2)在保證(1)的情況,如果還是這個(gè)錯(cuò)誤,那么通過上面的分析,其實(shí)也很好解決,怎么解決?

  spring在處理入?yún)⒌臅r(shí)候, 不是遇到MultipartFile相關(guān)就會(huì)先去校驗(yàn)么,OK,利用這個(gè),那么咱們可以改寫入?yún)ⅲㄖ苯咏邮赵膆ttp request),然后自己手動(dòng)去校驗(yàn)啊對(duì)吧,這不就繞過了。當(dāng)繞過這一步之后,springmvc會(huì)通過之前分析的代碼,對(duì)收到的請(qǐng)求進(jìn)行校驗(yàn)轉(zhuǎn)換,最終也會(huì)得到MultipartHttpServletRequest。修改如下:

復(fù)制代碼
@RequestMapping(value = "/upload", method = RequestMethod.POST)public ResultView upload(HttpServletRequest request) {    if (request instanceof MultipartHttpServletRequest) {        // process    }}復(fù)制代碼

OK, 這樣就通過了。

好啦,本篇就先寫到這兒,下篇將向大家談?wù)剆pringmvc上傳文件的效率問題。


發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 荆门市| 利津县| 天祝| 泗洪县| 水城县| 哈巴河县| 乌拉特前旗| 台东县| 内黄县| 饶阳县| 甘德县| 瓮安县| 祁连县| 华宁县| 丘北县| 敖汉旗| 沾化县| 山丹县| 鲁山县| 达拉特旗| 乌海市| 蓬溪县| 梁河县| 东乌珠穆沁旗| 白朗县| 福泉市| 铜陵市| 鸡东县| 康乐县| 当涂县| 大方县| 上栗县| 龙胜| 驻马店市| 南宁市| 平和县| 都江堰市| 德惠市| 车致| 泸水县| 绥棱县|