同步操作将从 huifer/Code-Analysis 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
本章将对 MultipartResolver 接口进行分析, MultipartResolver作用是判断 request是不是multipart/form-data类型,是则把request包装成MultipartHttpServletRequest。在MultipartResolver接口中定义了三个方法,具体代码如下:
public interface MultipartResolver {
boolean isMultipart(HttpServletRequest request);
MultipartHttpServletRequest resolveMultipart(HttpServletRequest request) throws MultipartException;
void cleanupMultipart(MultipartHttpServletRequest request);
}
下面对三个方法进行作用说明:
本节将介绍MultipartResolver 测试环境搭建,首先需要在build.gradle文件中添加依赖,具体依赖如下:
implementation 'commons-io:commons-io:2.4'
implementation 'commons-fileupload:commons-fileupload:1.3.1'
其次需要在applicationContext.xml文件中添加MultipartResolver 的实现类,具体代码如下:
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="utf-8"></property>
<property name="maxUploadSize" value="10485760000"></property>
<property name="maxInMemorySize" value="40960"></property>
</bean>
在这段代码中将MultipartResolver的实现类定义成CommonsMultipartResolver,并设置三个属性:
完成MultipartResolver实现类的定义和配置后需要编写Controller对象,具体代码如下:
@Controller
public class MultipartResolverController {
@PostMapping("/data")
public String data(
@RequestParam(value = "file",required = true) MultipartFile file
) {
return "hello";
}
}
完成Controller编写后需要进行接口测试,本次测试需要使用POSTMAN这个工具,具体测试接口信息如图所示:
在个请求中需要注意一点,请求参数file是一个文件类型并不是常规字符串,参数值可以选择任意的文件,当发送请求后会跳转到hello.jsp页面,具体返回值如下:
<html>
<head>
<title>Title</title>
</head>
<body>
<h3>hello-jsp</h3>
</body>
</html>
本节将对MultipartResolver的初始化进行分析,具体处理代码如下:
private void initMultipartResolver(ApplicationContext context) {
try {
this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class);
if (logger.isTraceEnabled()) {
logger.trace("Detected " + this.multipartResolver);
}
else if (logger.isDebugEnabled()) {
logger.debug("Detected " + this.multipartResolver.getClass().getSimpleName());
}
}
catch (NoSuchBeanDefinitionException ex) {
// Default is no multipart resolver.
this.multipartResolver = null;
if (logger.isTraceEnabled()) {
logger.trace("No MultipartResolver '" + MULTIPART_RESOLVER_BEAN_NAME + "' declared");
}
}
}
在这段代码中只提供了一种方式获取MultipartResolver对象,具体方式是通过名称+类型进行获取,这个获取方式对应了前文对于测试环境搭建中的配置信息:
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="utf-8"></property>
<property name="maxUploadSize" value="10485760000"></property>
<property name="maxInMemorySize" value="40960"></property>
</bean>
通过调试初始化方法可以看到multipartResolver的数据信息如下:
在这个信息中可以看到defaultEncoding、maxUploadSize和maxInMemorySize属性被设置到multipartResolver对象中。
本节将对CommonsMultipartResolver类进行分析,在CommonsMultipartResolver对象中最重要的方法是resolveMultipart,其他方法为其提供辅助,下面是resolveMultipart的具体代码:
@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());
}
}
在上述代码中可以发现主要目的是为了创建MultipartHttpServletRequest接口的实现类,该接口的实现类信息如图所示:
在resolveMultipart方法中具体创建的实例是DefaultMultipartHttpServletRequest,在创建过程中关于resolveLazily成员变量的数据内容会导致initializeMultipart方法的差异化,当resolveLazily为true时会进行如下操作:
需要注意在CommonsMultipartResolver类中resolveLazily数据默认值为false。下面发送一个请求,查看parsingResult变量和返回值的数据内容,具体信息如图所示:
在resolveMultipart方法调用过程中可以发现它需要使用parseRequest方法来创建(获取)MultipartParsingResult对象,下面将对该方法进行分析,具体处理代码如下:
protected MultipartParsingResult parseRequest(HttpServletRequest request) throws MultipartException {
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);
}
}
在parseRequest方法中主要处理流程如下:
下面将通过调试来对上述四个步骤中关键数据进行查看,首先是fileUpload对象,具体数据如下:
其次是fileItems的解析结果,具体数据填写:
最后是方法返回值具体数据如图所示:
通过上图可以发现在parseRequest方法的处理过程中主要目的是将请求中的文件数据转换成MultipartParsingResult对象的multipartFiles字段。最后在创建DefaultMultipartHttpServletRequest对象时也只是将数据从MultipartParsingResult搬运到DefaultMultipartHttpServletRequest。
本节将对StandardMultipartHttpServletRequest对象进行分析,在该对象中主要关注的方法是parseRequest,具体处理代码如下:
private void parseRequest(HttpServletRequest request) {
try {
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) {
if (filename.startsWith("=?") && filename.endsWith("?=")) {
filename = MimeDelegate.decode(filename);
}
files.add(part.getName(), new StandardMultipartFile(part, filename));
}
else {
this.multipartParameterNames.add(part.getName());
}
}
setMultipartFiles(files);
}
catch (Throwable ex) {
handleParseFailure(ex);
}
}
在parseRequest方法中主要目的是设置二个成员变量:multipartParameterNames和multipartFiles,该方法的主要操作流程如下:
如果在上述两个操作过程中出现异常将抛出MaxUploadSizeExceededException或者MultipartException异常,具体处理异常的代码如下:
protected void handleParseFailure(Throwable ex) {
String msg = ex.getMessage();
if (msg != null && msg.contains("size") && msg.contains("exceed")) {
throw new MaxUploadSizeExceededException(-1, ex);
}
throw new MultipartException("Failed to parse multipart servlet request", ex);
}
在MultiValueMap集合中关于数据信息Value的实际对象是StandardMultipartFile,关于它的定义信息如下:
private static class StandardMultipartFile implements MultipartFile, Serializable {
private final Part part;
private final String filename;
}
在StandardMultipartFile类中定义了两个属性:
本节将对MultipartResolver整体处理流程进行分析,当发起一个文件上传的请求后进入SpringMVC项目后会率先进入org.springframework.web.servlet.DispatcherServlet#doDispatch方法,在该方法中主要进行文件上传处理的方法是checkMultipart(request),具体代码如下:
protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
if (WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class) != null) {
if (request.getDispatcherType().equals(DispatcherType.REQUEST)) {
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 {
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;
}
在checkMultipart方法中主要处理处理如下:
在得到checkMultipart(request)方法处理后的request对象后需要进行的操作是寻找对应的HandlerMapping进行处理。
本章围绕MultipartResolver接口对该接口的二个实现类进行了相关分析,第一个类是CommonsMultipartResolver ,第二个类是StandardServletMultipartResolver ,此外对整体的处理流程也做了相关分析。
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。