深度解析源码之SpringMVC文件上传为什么要用POST请求还要设置请求头
从本篇文章开始,来逐步介绍里面每一步的细节处理流程。
首先看到doDispatch方法的第一个重要操作就是校验文件上传请求。代码如下:
这个方法是如何校验文件上传请求的呢? 在看这个方法之前,我们先来回忆回忆,之前在写文件上传前端代码的时候,有两个约定俗成的原则:
请求方式要是POST类型的
要设置enctype属性为"multipart/form-data"类型
我们带着这两个约定的原则来看看SpringMVC中是如何来判断文件上传请求的,打开checkMultipart方法的代码,核心代码如下:
在上面代码第2行这有个判断,调用了multipartResolver的isMultipart方法,这个对象我们在之前介绍Spring九大组件时提到过,它是SpringMVC九大组件中的其中一个,在SpringMVC九大组件初始化的时候,会从容器中去获取这个组件,如下:
如果我们在Spring容器中没有装配这个类型的Bean,那么应用就没办法支持SpringMVC的文件上传。 我们继续看上面的multipartResolver的isMultipart是干嘛用的呢?
先来看看官方的解释:
这个方法就是来看看request请求中的content-type是否为"multipart/form-data"类型的。也就是我们在form表单中设置的enctype属性。下面来看看这个方法的实现。
MultipartResolver本身是SpringMVC提供的一个文件上传的标准化接口,我们可以基于这个接口来实现自己的文件上传逻辑,但是在SpringMVC中,对这个接口提供了一个默认的实现,在spring-web这个模块中,对应的实现类的类名是CommonsMultipartResolver,我们来看看这个实现类里面的逻辑:
里面直接调用了commons-fileupload组件的isMultipartContent方法,这个方法是apache提供的一个文件上传组件,对应的实现如下:
我们看到,在66行,第一个判断逻辑是判断是否为POST请求,所以验证了之前我们提到的文件上传第一个原则,要是POST类型的请求。
如果是POST请求,将会再去调用isMultipartContent方法,校验HTTP请求头信息中的Context-Type属性,如下:
我们看到上面的if判断中,只是校验了Content-Type是否是"multipart/“开头的,但是并不是一定要是"multipart/form-data”,那就只有一种可能,就是还有其他类型的Content-Type可以支持文件上传了。
那么判断它是文件上传请求之后呢?
那当然是要解析文件上传请求了,也就是下面代码中的红框部分了
这个方法里面就是将普通的HttpServletRequest中的文件信息进行解析,然后封装为MultipartHttpServletRequest对象,代码如下:
默认不会走延迟解析这种方式,直接解析request请求,在这个parseRequest方法中就是一系列commons-fileupload组件的操作了,如下:
这个parseRequest方法再往里,就是从请求中读取文件输入流,然后封装到MultipartParsingResult中,最后再把MultipartParsingResult包装为一个文件请求对象返回。commons-fileupload中的操作,有兴趣可以留言我们再展开聊。
这一切准备就绪之后,就回到我们最开始的doDispatch方法中了,如下
如果返回的请求和原始请求不一样,就表示解析到了文件,再下面的步骤就会进行文件的写文件操作。
对于文件请求的判断我们就介绍到这,可以自己结合代码捋一捋。文章结尾我们再来聊一个问题:
文件上传为什么一定得是POST请求方式,Content-Type为什么一定得是"multipart/form-data"等方式?
请求用GET还是POST,就得看他们有什么区别,我们都知道,最大的一个区别就是GET请求能传输的数据一次性最多只能到1024字节,而POST请求则没有限制,所以一般传文件,请求数据肯定都相对比较大,就得用POST请求。
为什么Context-Type这个请求头就一定得是"multipart/form-data",这个没有为什么,就是"RFC1867"协议中定义的一个文件上传标准。也就是"互联网工程任务组"定义的一些标准属性,属于一些底层协议层面的标准,那么你上层应用想要用这种功能,就必须要遵守人家定义的这个标准。
码字实属不易,如果有收获,请动动手指,点个赞和关注鼓励一下呗!