根據源碼用HttpServletRequest獲取MultipartFile的問題


問題

由於某些原因,現在需要這樣的一個文件上傳接口,這個接口type(String)是必傳參數,photoFile(MultipartFile)是非必傳參數,即一般情況下需要接受兩個參數,分別為photoFile和type,但是偶爾只接受type參數,不需要起到上傳作用。

按常規寫法,photoFile參數的required配置設置為了false。

奈何調試時發現,photoFile的required配置是失效的。即下面的接口寫法,photoFile成了必傳參數。

@ResponseBody
@RequestMapping(value = "/upload")
public String uploadPics(@RequestPart(value = "photoFile", required = false) MultipartFile photoFile,
		@RequestParam(value = "type", required = true) String type, HttpServletRequest request) throws Exception {
		。。。
}

當模擬upload接口請求時,如果不攜帶photoFile參數,如上接口寫法報錯如下

org.springframework.web.multipart.MultipartException: The current request is not a multipart request
	at org.springframework.web.servlet.mvc.method.annotation.RequestPartMethodArgumentResolver.assertIsMultipartRequest(RequestPartMethodArgumentResolver.java:178) ~[spring-webmvc-4.0.2.RELEASE.jar:4.0.2.RELEASE]
	at org.springframework.web.servlet.mvc.method.annotation.RequestPartMethodArgumentResolver.resolveArgument(RequestPartMethodArgumentResolver.java:116) ~[spring-webmvc-4.0.2.RELEASE.jar:4.0.2.RELEASE]
	at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:79) ~[spring-web-4.0.2.RELEASE.jar:4.0.2.RELEASE]
	at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:157) ~[spring-web-4.0.2.RELEASE.jar:4.0.2.RELEASE]
	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:124) ~[spring-web-4.0.2.RELEASE.jar:4.0.2.RELEASE]
	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104) ~[spring-webmvc-4.0.2.RELEASE.jar:4.0.2.RELEASE]
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:749) ~[spring-webmvc-4.0.2.RELEASE.jar:4.0.2.RELEASE]
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:690) ~[spring-webmvc-4.0.2.RELEASE.jar:4.0.2.RELEASE]
	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:83) ~[spring-webmvc-4.0.2.RELEASE.jar:4.0.2.RELEASE]
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:945) ~[spring-webmvc-4.0.2.RELEASE.jar:4.0.2.RELEASE]
	...

方案

required配置失效,估計是沒有走required的相關流程。
我們根據所報的第三行錯誤,進入RequestPartMethodArgumentResolver.class的源碼的116行查看。

111		@Override
112		public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
113				NativeWebRequest request, WebDataBinderFactory binderFactory) throws Exception {
114
115			HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class);
116			assertIsMultipartRequest(servletRequest);
117
118			MultipartHttpServletRequest multipartRequest =
119				WebUtils.getNativeRequest(servletRequest, MultipartHttpServletRequest.class);
120
121			String partName = getPartName(parameter);
122			Object arg;
123
124			if (MultipartFile.class.equals(parameter.getParameterType())) {
125				Assert.notNull(multipartRequest, "Expected MultipartHttpServletRequest: is a MultipartResolver configured?");
126				arg = multipartRequest.getFile(partName);
			}
		  .....

再點擊116行,進入assertIsMultipartRequest方法

175		private static void assertIsMultipartRequest(HttpServletRequest request) {
176			String contentType = request.getContentType();
177			if (contentType == null || !contentType.toLowerCase().startsWith("multipart/")) {
178				throw new MultipartException("The current request is not a multipart request");
179			}
		}

很明顯,錯誤就是178行觸發的。而且在上層的幾個調用中也沒有涉及到required的流程。
那么問題來了, 接口偶爾需要起到上傳作用,如果不改變接口傳參形式,就只能改源碼了,很明顯,這不是好的方案。

那答案肯定就在源碼里了,我們很容易就想到可以在接口處完全去掉photoFile(MultipartFile)參數,然后把模仿源碼,從request里去獲取MultipartFile對象。

下面是根據源碼118-126行、176-179行修改的接口,經測試是可行的:

@ResponseBody
@RequestMapping(value = "/upload")
public String uploadPics(@RequestParam(value = "type", required = true) String type,
		HttpServletRequest request) {
	....
	// 檢測是否為上傳請求
	String contentType = request.getContentType();
	if (contentType != null && contentType.toLowerCase().startsWith("multipart/")) {
		MultipartHttpServletRequest multipartRequest =
				WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class);
		MultipartFile file = multipartRequest.getFile("file");   
		....
	}
	....
}

后記

問題的處理其實很簡單,但是這邊文章的記錄是為了另一件事。

當時實現這個功能時,發現常規寫法走不通,又不好改源碼,內心是有草泥馬奔騰而過的。

知道答案可能在源碼里,猜想可以去用request獲取MultipartFile對象,但是又覺得麻煩,不想去干這事,想着可能有更好的辦法,然后這事就拖着。

到后來問同事怎么解決,同事說那就從request獲取MultipartFile對象咯。😮

其實工作經常碰到這類事情,但是很多時候那些 “貌似遙遠的路途” 才是真正的捷徑 ~


免責聲明!

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



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