写在前面
传统的servlet处理request请求是,会自动帮我们封装get请求、post且Content-Type为application/x-www-form-urlencoded请求参数至request parameter map。但是当Content-Type为其他类型时,需要基于servlet的框架比如struts、spring来帮我们封装,不按的话得我们自己获取请求流来处理。
之所以会这样,是因为:Content-Type不是application/x-www-form-urlencoded时数据格式不固定,不一定是key=value对的方式,所以服务器无法知道具体的处理方式,所以只能通过获取原始数据流的方式来进行解析。
jquery在执行post请求时,会设置Content-Type为application/x-www-form-urlencoded,所以服务器能够正确解析,而使用原生ajax请求时,如果不显示的设置Content-Type,那么默认是text/plain,这时服务器就不知道怎么解析数据了,所以才只能通过获取原始数据流的方式来进行解析请求数据。
正文
springMvc内实现了个针对不同Content-Type请求的handler类,所以不需要我们关心即可随意获取request中的参数。
但是有一次用postMan使用form-data的post请求springMVC接口,后台无法获取参数和参数的值。
)
先来分析下为什么会出现这种情况,通过查看debug可以看到springMVC在此处开始处理前端的request,
下面是其他的filter,我们可以先不管他。从此源代码开始跟踪(DispatcherServlet(FrameworkServlet).service(HttpServletRequest, HttpServletResponse) line: 846 ):
入口是:org.springframework.web.servlet.FrameworkServlet.service(HttpServletRequest, HttpServletResponse),插一句:FrameworkServlet是Spring web框架的基本servlet实现类,service为实现了HttpServlet的处理http请求的入口。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
/*解释:
因为HttpServlet中是没有定义doPatch方法的,所以规定
if(是doPatch类型)
就交由本类(FrameworkServlet)内的doPatch方法运行。
else
调用父类(HttpServlet)的service方法。
*/
HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
processRequest(request, response);
}
else {
//此处httpMethod为post
super.service(request, response);
}
}javax.servlet.http.HttpServlet.service(HttpServletRequest, HttpServletResponse):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62/**
以下代码处理后最终还是走spring自己的dopost方法
**/
protected void service(HttpServletRequest req, HttpServletResponse resp)
/* */ throws ServletException, IOException
/* */ {
/* 700 */ String method = req.getMethod();
/* */
/* 702 */ if (method.equals("GET")) {
/* 703 */ long lastModified = getLastModified(req);
/* 704 */ if (lastModified == -1L)
/* */ {
/* */
/* 707 */ doGet(req, resp);
/* */ } else {
/* 709 */ long ifModifiedSince = req.getDateHeader("If-Modified-Since");
/* 710 */ if (ifModifiedSince < lastModified / 1000L * 1000L)
/* */ {
/* */
/* */
/* 714 */ maybeSetLastModified(resp, lastModified);
/* 715 */ doGet(req, resp);
/* */ } else {
/* 717 */ resp.setStatus(304);
/* */ }
/* */ }
/* */ }
/* 721 */ else if (method.equals("HEAD")) {
/* 722 */ long lastModified = getLastModified(req);
/* 723 */ maybeSetLastModified(resp, lastModified);
/* 724 */ doHead(req, resp);
/* */ }
/* 726 */ else if (method.equals("POST")) {
/* 727 */ doPost(req, resp);
/* */ }
/* 729 */ else if (method.equals("PUT")) {
/* 730 */ doPut(req, resp);
/* */ }
/* 732 */ else if (method.equals("DELETE")) {
/* 733 */ doDelete(req, resp);
/* */ }
/* 735 */ else if (method.equals("OPTIONS")) {
/* 736 */ doOptions(req, resp);
/* */ }
/* 738 */ else if (method.equals("TRACE")) {
/* 739 */ doTrace(req, resp);
/* */
/* */
/* */ }
/* */ else
/* */ {
/* */
/* */
/* 747 */ String errMsg = lStrings.getString("http.method_not_implemented");
/* 748 */ Object[] errArgs = new Object[1];
/* 749 */ errArgs[0] = method;
/* 750 */ errMsg = MessageFormat.format(errMsg, errArgs);
/* */
/* 752 */ resp.sendError(501, errMsg);
/* */ }
/* */ }
org.springframework.web.servlet.FrameworkServlet.doPost(HttpServletRequest, HttpServletResponse)
1
2
3
4
5
6
protected final void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}org.springframework.web.servlet.FrameworkServlet.processRequest(HttpServletRequest, HttpServletResponse):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
long startTime = System.currentTimeMillis();
Throwable failureCause = null;
//获取上个请求的localeContext和requestAttributes并整合
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
LocaleContext localeContext = buildLocaleContext(request);
RequestAttributes previousAttributes =RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
//大意:该处是为了处理异步请求的多线程上下文安全的传播
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
//大意:前面处理localeContext和requestAttributes备份
initContextHolders(request, localeContext, requestAttributes);
try {
//正在的处理入口
doService(request, response);
}
catch (ServletException ex) {
failureCause = ex;
throw ex;
}
catch (IOException ex) {
failureCause = ex;
throw ex;
}
catch (Throwable ex) {
failureCause = ex;
throw new NestedServletException("Request processing failed", ex);
}
finally {
resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
requestAttributes.requestCompleted();
}
if (logger.isDebugEnabled()) {
if (failureCause != null) {
this.logger.debug("Could not complete request", failureCause);
}
else {
if (asyncManager.isConcurrentHandlingStarted()) {
logger.debug("Leaving response open for concurrent processing");
}
else {
this.logger.debug("Successfully completed request");
}
}
}
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}org.springframework.web.servlet.DispatcherServlet.doService(HttpServletRequest, HttpServletResponse)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
if (logger.isDebugEnabled()) {
String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +
" processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
}
// Keep a snapshot of the request attributes in case of an include,
// to be able to restore the original attributes after the include.
Map<String, Object> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap<String, Object>();
Enumeration<?> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
}
// Make framework objects available to handlers and view objects.
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
if (inputFlashMap != null) {
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
}
request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
try {
//继续处理
doDispatch(request, response);
}
finally {
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
}org.springframework.web.servlet.DispatcherServlet.doDispatch(HttpServletRequest, HttpServletResponse):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90protected 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 {
//转换multipart request,如果不是multipart request,就用原本的
//可以猜想到这里面对request进行了处理,我们需要进去细看
//通过debug我们看到这里返回了原本的request,肯定是不对的,我们debug进去看看
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
//核心:获取该请求的HandlerMapping
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
//核心
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
//执行拦截PreHandle
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
//下一入口。
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}org.springframework.web.servlet.DispatcherServlet.checkMultipart(HttpServletRequest):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
//判断是否是Multipartrequest,看来是这里if没有为true,我们继续跟踪
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 (hasMultipartException(request) ) {
logger.debug("Multipart resolution failed for current request before - " +
"skipping re-resolution for undisturbed error rendering");
}
else {
try {
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;
}org.springframework.web.multipart.commons.CommonsMultipartResolver.isMultipart(HttpServletRequest)——-》org.apache.commons.fileupload.servlet.ServletFileUpload.isMultipartContent(HttpServletRequest)——》org.apache.commons.fileupload.FileUploadBase.isMultipartContent(RequestContext):
1
2
3
4
5
6
7
8
9
10
11public static final boolean isMultipartContent(RequestContext ctx) {
String contentType = ctx.getContentType();
if (contentType == null) {
return false;
}
//是否startwith multipart/
if (contentType.toLowerCase(Locale.ENGLISH).startsWith(MULTIPART)) {
return true;
}
return false;
}看到这里发现自己的ContentType竟然是:application/x-www-form-urlencoded,然而参数传送方式却是formdata,两边不一致,导致后面无法没有获取到参数的值。原来postman默认给contentType填充了值,我们其去掉即可,去掉之后再次请求,发现可以取到参数的值了!
现在我们继续返回来分析为什么需要让这段if返回ture才能取到值呢?
org.springframework.web.servlet.DispatcherServlet.checkMultipart(HttpServletRequest):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
//判断是否是Multipartrequest,看来是这里if没有为true,我们继续跟踪
if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
//判断当前request是否是MultipartHttpServletRequest,如果是则直接跳过不解析处理该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 (hasMultipartException(request) ) {
//如果已经报错,则也不继续解析该request
logger.debug("Multipart resolution failed for current request before - " +
"skipping re-resolution for undisturbed error rendering");
}
else {
try {
//开始解析multpartrequest
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;
}重头戏org.springframework.web.multipart.commons.CommonsMultipartResolver.resolveMultipart(HttpServletRequest):
返回了MultipartHttpServletRequest的实现类:DefaultMultipartHttpServletRequest
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public MultipartHttpServletRequest resolveMultipart(final HttpServletRequest request) throws MultipartException {
Assert.notNull(request, "Request must not be null");
if (this.resolveLazily) {
//配置为false,所以先忽略
//延迟解析,原理:重写了DefaultMultipartHttpServletRequest实体类里只存入request,实现新initializeMultipart方法。需要后面手工调用initializeMultipart,才能解析request
return new DefaultMultipartHttpServletRequest(request) {
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());
}
}之所以是走这个resolve,是因为项目配置文件内配置了该bean:
1
2
3
4
5
6<!-- 文件上传相关 解析器 -->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!--one of the properties available;the maximum upload size in bytes 10M -->
<property name="maxUploadSize" value="10485760" />
</bean>org.springframework.web.multipart.commons.CommonsMultipartResolver.parseRequest(HttpServletRequest)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21protected MultipartParsingResult parseRequest(HttpServletRequest request) throws MultipartException {
//获取encoding ;第一位:request。第二位:bean配置。第三位:jar包默认:ISO-8859-1
String encoding = determineEncoding(request);
//获取文件上传处理类
FileUpload fileUpload = prepareFileUpload(encoding);
try {
//
List<FileItem> fileItems = ((ServletFileUpload) fileUpload).parseRequest(request);
//
return parseFileItems(fileItems, encoding);
}
catch (FileUploadBase.SizeLimitExceededException ex) {
throw new MaxUploadSizeExceededException(fileUpload.getSizeMax(), ex);
}
catch (FileUploadBase.FileSizeLimitExceededException ex) {
throw new MaxUploadSizeExceededException(fileUpload.getFileSizeMax(), ex);
}
catch (FileUploadException ex) {
throw new MultipartException("Failed to parse multipart servlet request", ex);
}
}org.apache.commons.fileupload.FileUploadBase.parseRequest(RequestContext)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54public List<FileItem> parseRequest(RequestContext ctx)
throws FileUploadException {
List<FileItem> items = new ArrayList<FileItem>();
boolean successful = false;
try {
//根据RequestContext数据源得到解析后的数据集合 FileItemIterator
FileItemIterator iter = getItemIterator(ctx);
//
FileItemFactory fac = getFileItemFactory();
if (fac == null) {
throw new NullPointerException("No FileItemFactory has been set.");
}
//遍历FileItemIterator中的每个item,类型为FileItemStreamImpl,使用FileItemFactory工厂类来将每个FileItemStreamImpl转化成最终的FileItem
while (iter.hasNext()) {
//每次调用FileItemIteratorImpl的hasNext()方法,会创建一个新的FileItemStreamImpl赋值给FileItemStreamImpl属性
//每次调用FileItemIteratorImpl的next()方法,就会返回当前FileItemStreamImpl属性的值
//创建的每个FileItemStreamImpl都会共享FileItemIteratorImpl的MultipartStream总流,仅仅更新了要读取的起始位置
final FileItemStream item = iter.next();
// Don't use getName() here to prevent an InvalidFileNameException.
final String fileName = ((FileItemIteratorImpl.FileItemStreamImpl) item).name;
FileItem fileItem = fac.createItem(item.getFieldName(), item.getContentType(),
item.isFormField(), fileName);
items.add(fileItem);
try {
Streams.copy(item.openStream(), fileItem.getOutputStream(), true);
} catch (FileUploadIOException e) {
throw (FileUploadException) e.getCause();
} catch (IOException e) {
throw new IOFileUploadException(format("Processing of %s request failed. %s",
MULTIPART_FORM_DATA, e.getMessage()), e);
}
final FileItemHeaders fih = item.getHeaders();
fileItem.setHeaders(fih);
}
successful = true;
//这个时候已经把我们的参数和值读取完成了。见下图
return items;
} catch (FileUploadIOException e) {
throw (FileUploadException) e.getCause();
} catch (IOException e) {
throw new FileUploadException(e.getMessage(), e);
} finally {
if (!successful) {
for (FileItem fileItem : items) {
try {
fileItem.delete();
} catch (Throwable e) {
// ignore it
}
}
}
}
}org.apache.commons.fileupload.FileUploadBase.FileItemIteratorImpl.FileItemIteratorImpl(FileUploadBase, RequestContext):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70FileItemIteratorImpl(RequestContext ctx)
throws FileUploadException, IOException {
//文件处理实现内部类
if (ctx == null) {
throw new NullPointerException("ctx parameter");
}
//再次判断ContentType
String contentType = ctx.getContentType();
if ((null == contentType)
|| (!contentType.toLowerCase(Locale.ENGLISH).startsWith(MULTIPART))) {
throw new InvalidContentTypeException(
format("the request doesn't contain a %s or %s stream, content type header is %s",
MULTIPART_FORM_DATA, MULTIPART_MIXED, contentType));
}
//获取输入流
InputStream input = ctx.getInputStream();
"deprecation") // still has to be backward compatible (
final int contentLengthInt = ctx.getContentLength();
final long requestSize = UploadContext.class.isAssignableFrom(ctx.getClass())
// Inline conditional is OK here CHECKSTYLE:OFF
? ((UploadContext) ctx).contentLength()
: contentLengthInt;
// CHECKSTYLE:ON
//判断输入流是否超限
if (sizeMax >= 0) {
if (requestSize != -1 && requestSize > sizeMax) {
throw new SizeLimitExceededException(
format("the request was rejected because its size (%s) exceeds the configured maximum (%s)",
Long.valueOf(requestSize), Long.valueOf(sizeMax)),
requestSize, sizeMax);
}
input = new LimitedInputStream(input, sizeMax) {
protected void raiseError(long pSizeMax, long pCount)
throws IOException {
FileUploadException ex = new SizeLimitExceededException(
format("the request was rejected because its size (%s) exceeds the configured maximum (%s)",
Long.valueOf(pCount), Long.valueOf(pSizeMax)),
pCount, pSizeMax);
throw new FileUploadIOException(ex);
}
};
}
String charEncoding = headerEncoding;
if (charEncoding == null) {
charEncoding = ctx.getCharacterEncoding();
}
//获取boundary,见下图
boundary = getBoundary(contentType);
if (boundary == null) {
throw new FileUploadException("the request was rejected because no multipart boundary was found");
}
notifier = new MultipartStream.ProgressNotifier(listener, requestSize);
try {
multi = new MultipartStream(input, boundary, notifier);
} catch (IllegalArgumentException iae) {
throw new InvalidContentTypeException(
format("The boundary specified in the %s header is too long", CONTENT_TYPE), iae);
}
multi.setHeaderEncoding(charEncoding);
skipPreamble = true;
//findNextItem()方法就是创建新的FileItemStreamImpl来替代当前的FileItemStreamImpl,并更新起始位置。此处为第一次调用
findNextItem();
}org.apache.commons.fileupload.FileUploadBase.FileItemIteratorImpl.findNextItem():
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69private boolean findNextItem() throws IOException {
if (eof) {
return false;
}
if (currentItem != null) {
currentItem.close();
currentItem = null;
}
for (;;) {
boolean nextPart;
if (skipPreamble) {
nextPart = multi.skipPreamble();
} else {
nextPart = multi.readBoundary();
}
if (!nextPart) {
if (currentFieldName == null) {
// Outer multipart terminated -> No more data
eof = true;
return false;
}
// Inner multipart terminated -> Return to parsing the outer
multi.setBoundary(boundary);
currentFieldName = null;
continue;
}
FileItemHeaders headers = getParsedHeaders(multi.readHeaders());
if (currentFieldName == null) {
// We're parsing the outer multipart
String fieldName = getFieldName(headers);
if (fieldName != null) {
String subContentType = headers.getHeader(CONTENT_TYPE);
if (subContentType != null
&& subContentType.toLowerCase(Locale.ENGLISH)
.startsWith(MULTIPART_MIXED)) {
currentFieldName = fieldName;
// Multiple files associated with this field name
byte[] subBoundary = getBoundary(subContentType);
multi.setBoundary(subBoundary);
skipPreamble = true;
continue;
}
//获取header里的文件名称。
//如果为null,则为普通form表单(不是file)
String fileName = getFileName(headers);
currentItem = new FileItemStreamImpl(fileName,
fieldName, headers.getHeader(CONTENT_TYPE),
fileName == null, getContentLength(headers));
currentItem.setHeaders(headers);
notifier.noteItem();
itemValid = true;
return true;
}
} else {
String fileName = getFileName(headers);
if (fileName != null) {
currentItem = new FileItemStreamImpl(fileName,
currentFieldName,
headers.getHeader(CONTENT_TYPE),
false, getContentLength(headers));
currentItem.setHeaders(headers);
notifier.noteItem();
itemValid = true;
return true;
}
}
multi.discardBodyData();
}
}org.apache.commons.fileupload.FileUploadBase.getFileName(String):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25private String getFileName(String pContentDisposition) {
String fileName = null;
if (pContentDisposition != null) {
String cdl = pContentDisposition.toLowerCase(Locale.ENGLISH);
if (cdl.startsWith(FORM_DATA) || cdl.startsWith(ATTACHMENT)) {
ParameterParser parser = new ParameterParser();
parser.setLowerCaseNames(true);
// Parameter parser can handle null input
//封装header为MAP:
Map<String, String> params = parser.parse(pContentDisposition, ';');
if (params.containsKey("filename")) {
fileName = params.get("filename");
if (fileName != null) {
fileName = fileName.trim();
} else {
// Even if there is no value, the parameter is present,
// so we return an empty file name rather than no file
// name.
fileName = "";
}
}
}
}
return fileName;
}org.apache.commons.fileupload.ParameterParser.parse(char[], int, int, char):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45public Map<String, String> parse(
final char[] charArray,
int offset,
int length,
char separator) {
if (charArray == null) {
return new HashMap<String, String>();
}
HashMap<String, String> params = new HashMap<String, String>();
this.chars = charArray;
this.pos = offset;
this.len = length;
String paramName = null;
String paramValue = null;
while (hasChar()) {
paramName = parseToken(new char[] {
'=', separator });
paramValue = null;
if (hasChar() && (charArray[pos] == '=')) {
pos++; // skip '='
paramValue = parseQuotedToken(new char[] {
separator });
if (paramValue != null) {
try {
paramValue = MimeUtility.decodeText(paramValue);
} catch (UnsupportedEncodingException e) {
// let's keep the original value in this case
}
}
}
if (hasChar() && (charArray[pos] == separator)) {
pos++; // skip separator
}
if ((paramName != null) && (paramName.length() > 0)) {
if (this.lowerCaseNames) {
paramName = paramName.toLowerCase(Locale.ENGLISH);
}
params.put(paramName, paramValue);
}
}
return params;
}
org.springframework.web.multipart.commons.CommonsFileUploadSupport.parseFileItems(List
, String): 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54protected MultipartParsingResult parseFileItems(List<FileItem> fileItems, String encoding) {
MultiValueMap<String, MultipartFile> multipartFiles = new LinkedMultiValueMap<String, MultipartFile>();
Map<String, String[]> multipartParameters = new HashMap<String, String[]>();
Map<String, String> multipartParameterContentTypes = new HashMap<String, String>();
// Extract multipart files and multipart parameters.
for (FileItem fileItem : fileItems) {
if (fileItem.isFormField()) {
//判断是不是file
String value;
String partEncoding = determineEncoding(fileItem.getContentType(), encoding);
if (partEncoding != null) {
try {
value = fileItem.getString(partEncoding);
}
catch (UnsupportedEncodingException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Could not decode multipart item '" + fileItem.getFieldName() +
"' with encoding '" + partEncoding + "': using platform default");
}
value = fileItem.getString();
}
}
else {
value = fileItem.getString();
}
String[] curParam = multipartParameters.get(fileItem.getFieldName());
if (curParam == null) {
// simple form field
//数组【0】
multipartParameters.put(fileItem.getFieldName(), new String[] {value});
}
else {
// array of simple form fields
//数组增加
String[] newParam = StringUtils.addStringToArray(curParam, value);
multipartParameters.put(fileItem.getFieldName(), newParam);
}
//封装field的ContentType
multipartParameterContentTypes.put(fileItem.getFieldName(), fileItem.getContentType());
}
else {
// multipart file field
CommonsMultipartFile file = createMultipartFile(fileItem);
multipartFiles.add(file.getName(), file);
if (logger.isDebugEnabled()) {
logger.debug("Found multipart file [" + file.getName() + "] of size " + file.getSize() +
" bytes with original filename [" + file.getOriginalFilename() + "], stored " +
file.getStorageDescription());
}
}
}
return new MultipartParsingResult(multipartFiles, multipartParameters, multipartParameterContentTypes);
}至此,整个request的请求流封装完毕,被包装为org.springframework.web.multipart.MultipartHttpServletRequest(调用栈开始回归:10—》9—-》7)返回并代替原来的servletRequest继续下面的流程。
并最终返回给action的request供业务层使用