前言
在上篇文档,我们分析了如何使用Spring MVC进行文件的上传和下载,也分析了一些常用的类和接口,那么这些请求和解析器是如何工作的呢?
核心类
Part接口
Part是javax.servlet.http包下的一个接口,servlet3.0加入,此类用于封装multipart/form-data请求时,上传文件的请求体。
public interface Part {
InputStream getInputStream() throws IOException;
String getContentType();
String getName();
String getSubmittedFileName();
long getSize();
void write(String var1) throws IOException;
void delete() throws IOException;
String getHeader(String var1);
Collection<String> getHeaders(String var1);
Collection<String> getHeaderNames();
}
ApplicationPart
ApplicationPart是Tomcat中的类,实现了Part接口,是文件上传时,会将文件封装为此对象。
它有两个重要的属性:
private final FileItem fileItem;
private final File location;
FileItem封装了文件信息。
location表示文件实际对象,当上传文件时,会先存放到Tomcat的临时目录。
C:\Users\Administrator\AppData\Local\Temp\tomcat.9111.2463429038111242355\work\Tomcat\localhost\ROOT\upload_4b2ad82c_e0c0_45f3_8913_c2daa6234881_00000006.tmp
源码分析
1. 检查Multipart
所有的请求都由DispatcherServlet来调度,所以首先还是进入到其doDispatch方法。
doDispatch首先会进入到checkMultipart方法。
checkMultipart会调用DispatcherServlet的组件多部分请求解析器StandardServletMultipartResolver进行解析。
protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
// 1. 解析器存在,并且是multipart请求,也就是contentType包含了multipart/
if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
// 2. 检查Request 能不能转化为MultipartHttpServletRequest
if (WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class) != null) {
if (DispatcherType.REQUEST.equals(request.getDispatcherType())) {
}
// 3. 检查是否有MultipartException
} else if (this.hasMultipartException(request)) {
} else {
try {
// 4. 解析器解析请求
return this.multipartResolver.resolveMultipart(request);
} catch (MultipartException var3) {
}
}
return request;
}
2. 创建StandardMultipartHttpServletRequest对象
在第一步中,是multipart请求,则会进入到解析器中。
StandardServletMultipartResolver的机械方法,会直接new一个StandardMultipartHttpServletRequest请求对象。
public MultipartHttpServletRequest resolveMultipart(HttpServletRequest request) throws MultipartException {
return new StandardMultipartHttpServletRequest(request, this.resolveLazily);
}
StandardMultipartHttpServletRequest的构造方法中,如果没有配置延迟解析,直接进入到parseRequest方法解析请求。
public StandardMultipartHttpServletRequest(HttpServletRequest request, boolean lazyParsing) throws MultipartException {
super(request);
if (!lazyParsing) {
this.parseRequest(request);
}
}
3. 获取Parts
parseRequest方法di异步会从request中获取Part对象。
这里面的逻辑比较复杂,代码也很多,大概就是读取的 tomcat 放到服务器上的临时文件的文件流,获取这个文件的信息,并转为相关的文件信息对象FileItem 和文件对象。
最后这里的Parts包含了以下信息。
4. parseRequest
parseRequest方法主要是,将一些文件信息,设置到请求对象中。
// 解析请求
private void parseRequest(HttpServletRequest request) {
try {
// 1. request.getParts(),用于获取使用multipart/form-data格式传递的http请求的请求体,通常用于获取上传文件信息。
Collection<Part> parts = request.getParts();
// 2. 创建multipart参数的名称集合,MultipartFile对象集合
this.multipartParameterNames = new LinkedHashSet<>(parts.size());
MultiValueMap<String, MultipartFile> files = new LinkedMultiValueMap<>(parts.size());
// 3. 循环文件信息,并保存到当前请求StandardMultipartHttpServletRequest对象中
for (Part part : parts) {
// 从消息头Content-Disposition获取 文件描述 form-data; name="file"; filename="01053230.zip"
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);
}
// 4. 添加文件名= MultipartFile对象,键值对,放入到MultiValueMap<String, MultipartFile> 集合中
files.add(part.getName(), new StandardMultipartFile(part, filename));
}
else {
// 5. 当前类添加multipart 参数集合
this.multipartParameterNames.add(part.getName());
}
}
//5. MultiValueMap<String, MultipartFile> 集合设置到当前类中
setMultipartFiles(files);
}
catch (Throwable ex) {
handleParseFailure(ex);
}
}
经过检查解析,最终我们的请求对象变为了StandardMultipartHttpServletRequest,其中封装了文件信息。
4. 执行控制器方法
doDispatch接着往下执行,进入到控制器执行器,开始执行控制器方法。
经过其他组件处理,就到了我们的上传接口中。控制器接收到我们的文件对象,往下执行就完成了上传文件的流程。
版权声明:本文不是「本站」原创文章,版权归原作者所有 | 原文地址: