mint mvc文件上传功能——原理篇

mint mvc上传文件的实现封装在mint.mvc.core.upload包内,见下图:

mint mvc文件上传功能——原理篇_第1张图片

mint.mvc.core.upload包的结构

一张大图弥补我拙劣的语言表达能力:

mint mvc文件上传功能——原理篇_第2张图片

下面来细说upload包中6个类(也就是六个小图···)。

MultipartConfig

mint mvc文件上传功能——原理篇_第3张图片

MultipartConfig注解是用来配置文件上传的处理信息的,用法如下:

@MultipartConfig(attributeName = "files", limitSize = 1024*1024*1024, tempFilePath = "D:/upload")
@Mapping(urls={"/index", "/index/{id}"}, method="post")
public String index(Integer id, String name, String username, MultipartParameter[] files, HttpServletRequest request){
	return "index";
}
暂时有4个配置项:
  1. tempFilePath:临时文件存放目录
  2. attributeName:当文件上传完毕后,可以通过request.getAttribute("[attributeName]")方法获得上传文件(和普通域)的数组
  3. maxRequestSize:请求Content-length的最大长度
  4. limitSize:每个文件或普通域的最大长度

MultipartHttpServletRequest

mint mvc文件上传功能——原理篇_第4张图片

MultipartHttpServletRequest继承javax.servlet.http.HttpServletRequestWrapper,是用来封装多媒体表请求。

MultipartHttpServletRequest重写了HttpServletRequestWrapper 的 getPart、getParts、getParameter、getParameterMap方法。并且添加下面的几个方法:

  1. public MultipartParameter getMultipartParameter(String name):获取多媒体表单的参数,包括多媒体(文件)和普通参数
  2. public MultipartParameter[] getMultipartParameters():获取多媒体表单的所有参数,包括多媒体(文件)和普通参数
  3. public Set<String> getParameterNamesSet():返回所有参数名

MultipartParameter

mint mvc文件上传功能——原理篇_第5张图片

MultipartParameter用来封装多媒体表单的参数,每一个文件域或者文本域封装为一个MultipartParameter对象。该接口继承javax.servlet.http.Part,并且额外声明4个方法:

  1. public boolean isFile():判断当前参数是否为文件
  2. public String getParameterValue():获取文本域参数的值
  3. public File getTempFile():获取上传的临时文件
  4. public String getFilename():获取上传文件在上传时的文件名

DefaultMultipartParameter

mint mvc文件上传功能——原理篇_第6张图片

DefaultMultipartParameter是MultipartParameter的一个实现,详见MultipartParameter和javax.servlet.http.Part

FileUpload

mint mvc文件上传功能——原理篇_第7张图片

FileUpload是文件上传的工具类,介绍它的两个方法:

  1. public void upload(String tempFilePath, String attributeName, long limitSize, AsyncListener listener):上传文件。tempFilePath、attributeName、limitSize和MultipartConfig的配置项对应,listener声明了异步上传完成之后的回调方法。
  2. public static boolean upload(String tempFilePath, String attributeName, long limitSize, AsyncContext acontext, Object lock):前三个参数对应MultipartConfig的配置项。acontext是servlet3的异步处理上下文,lock如果想同步上传文件,可以在上传文件开始时阻塞当前线程,当文件上传完毕后,上传线程会唤醒被阻塞的线程,以继续运行

UploadExecutor

UploadExecutor是上传文件的真正执行者。负责多媒体表单的解析,把解析得到的参数封装到一个MultipartParamter数组里,最后调用request的SetAttribute方法,以指定attributeName把MultipartParamter数组保存request内。

文件上传的过程

上传的宏观过程

mint mvc文件上传功能——原理篇_第8张图片

如图所示,当接到头部声明有形如"Content-Type:multipart/form-data; boundary=----WebKitFormBound"的请求时,mint-mvc就会开启线程处理多媒体参数,并阻塞当前前端控制器的线程,待多媒体参数解析完毕后,唤醒前端控制器的线程。

以往文件上传的情况是,servlet应用服务器会包办文件上传这个过程,直到把请求数据接受完毕才调用指定的servlet,这样的做法有不少缺点:

  1. 一个根本不是设计来接受文件的servlet,也不会拒绝一个多媒体post请求,直到把请求数据接受完毕才调用指定的servlet。这样做是很浪费资源的,甚至是很危险的
  2. 在servlet3.0之前,一个处理上传头像的servlet,也可能自作多情地去接受一个几GB的windows 安装镜像,并且接受完毕才通知你,实在无法忍受

mint mvc如此实现文件上传,是试图用servlet3的异步特性,迫使servelt 服务器把文件上传的过程完全交给mint mvc或者使用者来处理,这样做希望达到如下效果:

  1. servlet 应用服务器只负责接受多媒体请求的头部,然后就把剩下的工作交给servlet处理。servlet根据头部描述决定请求体是否要继续接受,比如说content-length是否超出限制,甚至还可以要求客户端把表单中包含的文件的简要信息封装在头部,以便后端进行更加细粒度的判断
  2. 不打算处理多媒体参数的servlet可以提前拒绝 post过来的多媒体请求

实际上有点遗憾,servlet3并没有规定在异步功能开启时,文件接收由谁来完成——servlet服务器还是servlet。所以不同servlet服务器有不同的实现。测试中发现tomcat7在开启异步功能时,会把文件上传的工作交给servlet处理,而jetty则不会,至于其他服务器就不知道了

解析文件的过程

解析文件和参数的过程这里只做简单介绍。

先看看一个包含多个文件域和文本域的http请求头的结构:

mint mvc文件上传功能——原理篇_第9张图片

再来看对应请求体的结构(请忽略行号):

mint mvc文件上传功能——原理篇_第10张图片

请求头中的 boundary指的是请求体中的分隔符,但是请求体中的分隔符前面还要多“--”两个字符。请求体最后一个分隔符末尾还额外添加“--”两个字符。

请求体中分隔符和参数描述信息后面还有回车符(\r)和换行符(\n),最后一个分隔符后面有一个换行符(\n)。每个参数的描述头和参数值之间用回车换行符隔开。这些字符作为字符串输出时,看到的效果就是换行。提交数据实际上的格式是这样的:

综上所述,假如HTTP头指定的分隔符是“xxxx”,那么实际上请求体内的数据的格式是这样的:

--xxxx\r\n\r\n<数据>\r\n--xxxx\r\n\r\n<数据>\r\n--xxxx--\n

然后可以知道从请求体中解析参数的大致过程如下:

mint mvc文件上传功能——原理篇_第11张图片

至于代码怎么写,在此就不说了。

项目地址:http://git.oschina.net/895925636/mint-mvc

收录地址:http://www.oschina.net/p/mint-mvc

博客地址:http://www.wemakers.net/home/blog?cate=1001

(完)

你可能感兴趣的:(java文件上传,mint-mvc)