先说结论:
由于使用多文件上传使用了MultipartFile类来接收前端打包传输的经过FormData()处理过后Form表格,但是在MultipartFile上传之前会经过 org.springframework.web.multipart.commons.CommonsMultipartResolver
这个包进行解析,而其中
spring对其设置的默认限制大小为1M,需要更改此设置才能够上传大文件!!!
因此需要在mvc配置中加入以下配置信息:
<!-- 定义文件上传解析器 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 设定默认编码 -->
<property name="defaultEncoding" value="UTF-8"></property>
<!-- 设定文件上传的最大值为1GB -->
<property name="maxUploadSize" value="1073741824"></property>
<!--设定文件上传时写入内存的最大值,如果小于这个参数不会生成临时文件,默认为10240-->
<property name="maxInMemorySize" value="40960"></property>
<!-- 上传文件的临时路径 -->
<property name="uploadTempDir" value="./upTemp"></property>
<!--延迟文件解析-->
<property name="resolveLazily" value="true"/>
</bean>
ps:会导致该错误的原因还有前端form表单的文件中,只设置了id属性,没有设置name属性
以下是debug的漫长过程:
在网页中,我使用ajax对数据进行传输,前端内容大致如下:
<form id="upload_form" enctype="multipart/form-data">
<div>
<div><label for="image_file" style="color: wheat;">请选择投稿视频文件</label></div>
<div><input type="file" name="files" id="video"/></div>
</div>
<!--进度条-->
<div class="progress">
<div class="progress-bar" id="progress-bar"></div>
</div>
<div>
<div><label for="image_file" style="color: wheat;">请选择投稿封面壁纸</label></div>
<div><input type="file" name="files" id="img"/></div>
</div>
<div>
<div><label for="image_file" style="color: wheat;">请输入视频标题</label></div>
<div><input type="text" name="title" id="biaoti"/></div>
</div>
<div>
<div><label for="image_file" style="color: wheat;">请输入视频描述</label></div>
<div><textarea rows="13" name="describe" id="miaoshu" cols="70"></textarea></div>
</div>
<div>
<div><label for="image_file" style="color: wheat;">请选择投放区域</label></div>
<div>
<label><input name="vType" type="radio" value="1" checked/>动画 </label>
<label><input name="vType" type="radio" value="2"/>MAD </label>
<label><input name="vType" type="radio" value="3"/>音乐 </label>
<label><input name="vType" type="radio" value="1"/>影视 </label>
<label><input name="vType" type="radio" value="2"/>原创 </label>
</div>
</div>
<div>
<input type="button" value="立即投稿" id="tougao"/> <input type="button" id="fanhui" value="取消返回"/>
</div>
</form>
js的ajax大致如下:
$("#tougao").click(function () {
$("#tougao").attr('disabled', true); //将button变成不可点击
// forData为form的表单
var formData = new FormData($("#upload_form")[0]);
$.ajax({
url: 'ajaxvideoFileTop',
type: 'POST',
data: formData,
async: true, //设置为同步
cache: false,
contentType: false,
processData: false,
success: function (returndata) {
$("#tougao").attr('disabled',false); //将button变成可点击
},
error: function (returndata) {
console.log("returndata is :" + returndata)
alert(returndata);
},
xhr: function () {
myXhr = $.ajaxSettings.xhr();
if (myXhr.upload) { //检查upload属性是否存在
//绑定progress事件的回调函数
myXhr.upload.addEventListener('progress', progressHandlingFunction, false);
}
return myXhr; //xhr对象返回给jQuery使用
}
});
// 上传进度数字显示
function progressHandlingFunction(event) {
var loaded = Math.floor(100 * (event.loaded / event.total)); //已经上传的百分比
$("#progress-bar").html(loaded + "%").css("width", loaded + "%");
}
});
后端的controller为:
@RequestMapping(value = "ajaxvideoFileTop", method = RequestMethod.POST)
public String ajaxVideoup(@RequestParam("files") MultipartFile[] files,
@RequestParam String title,
@RequestParam String describe,
@RequestParam String vType,
HttpServletRequest request) {
...}
前端的摸查
初次触发ajxa的提交后报错误400 合理怀疑前端页面数据打包格式不正确,于是调出 new FormData($("#upload_form")[0])
的值进行查看:
看起来数据打包没什么问题,上传的两个文件都在,还是找不到原因。
查看tomcat后台也无任何报错或者空指针,于是推测是tomcat接收不了,但是在网上查询后发现,由于我使用的是IDEA中设置的tomcat,所以应该是没有限制上传文件大小的......
实在想不明白,尝试使用fiddle抓取网页的request
传输的表单文件完好,name属性也设置正确这里开始排除前端的原因了
后端的摸查
由于后端没有任何反应,我怀疑是在 @RequestMapping
这就匹配不上了,在网上重新回忆了下mvc的执行流程大致如下:
DispatcherServlet
doService
doDispatch
- mappedHandler 这里会反射控制器方法
handle
- HandlerAdapter ha 控制器的方法是在这里调用的
.....
查看mappedHandle中的handler发现其实是找到控制器方法的了
继续往下
查询spring API document
到此为止,也不是mappedHandler的问题,那就只剩下param匹配的问题了,但是我反复对比也没能发现与spring文档中的说明有任何的出入
于是开始死马当活马医,在 org.springframework.web.multipart
包下查看 org.springframework.web.multipart.MultipartFile
的加载过程
知道我注意到同包下有这些的存在:
抓住Resolver这个字眼 查看是什么类实现了这个MultipartResolver接口查看里面有没有声明什么方法,抓到了一点线索:
可见这里的报错中存在一个超出文件大小限制的说法
因此我猜测在某个实现类中应该会存在这个大小限制的变量,于是我开始查找MultipartResolver接口
- 虽然JAVA8以后能在接口中写入一些静态常量,但是这些老接口应该是不会这样做的,所以这里我要找它的实现类
先随便进入一个实现类查看,该类的说明:
这个说明可以通过用它设置bean来设置文件上传的最大大小,总算是给我逮到了,继续看它的父类
可以大致得知这是设置上传File文件的一些基础参数用的,用size作为关键字眼继续搜寻,结果找到了对应的get set方法,这里就是可以给bean设置的依据
这些应该bingo了??
再返回MultipartResolver接口查看另外一个实现类
可知该类也可以实现设置上传文件大小,只需要在web.xml中设置MultipartConfig的参数就可以了
综上相当于这两个类给了我们两种方法去设置文件上传的最大体积,最后给出导致我一直400的罪魁祸首 maxFileSize
:
这是利用注解声明最大上传体积的方法
至此找到罪魁祸首,我这里就直接利用spring的bean给上述的注入CommonsMultipartResolver:
<!-- 定义文件上传解析器 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 设定默认编码 -->
<property name="defaultEncoding" value="UTF-8"></property>
<!-- 设定文件上传的最大值为1GB -->
<property name="maxUploadSize" value="1073741824"></property>
<!--设定文件上传时写入内存的最大值,如果小于这个参数不会生成临时文件,默认为10240-->
<property name="maxInMemorySize" value="40960"></property>
<!-- 上传文件的临时路径 -->
<property name="uploadTempDir" value="./upTemp"></property>
<!--延迟文件解析-->
<property name="resolveLazily" value="true"/>
</bean>
通过postman打包post数据和ajax上传,全部都没问题了,总算解决了
总结
还是对spring了解的不够透彻,文档属于还是没看明白,想到文件上传限制,想到了tomcat却没想到是spring的问题,这次debug查阅了大量资料和浪费了大量时间,希望以后能记得这种流程,下次应该就不会这么折磨了?(大概把)
补充:
关于org.apache.commons.fileupload
和org.springframework.web.multipart.commons.CommonsMultiFile
会产生冲突的问题
本质都是使用fileupload进行配置的,如果使用了CommonsMultiFile的配置后会覆盖掉一部分fileupload的配置,导致request不能被解析。
因此如果在bean中配置了org.springframework.web.multipart.commons.CommonsMultipartResolver
,则request中的多文件上传格式会被改变,不能通过ServletFileUpload的.parseRequest
去解析request了,它已经被装载成MultipartFile
格式可以被直接读取了