表单的文件信息使用serialize()序列化之后传参引发问题,需要使用FormData对象封装form表单。
jsp:
1 | <form id="addUserForm" enctype="multipart/form-data"> |
1 | function user_insert() { |
需要添加form属性enctype=”multipart/form-data”。
需要几点:
1. method=”post” 是必须的
2. enctype=”multipart/form-data” 是必须的,表示提交二进制文件
3. accept=”image/*” 表示只选择图片
注意:这里的form表单没有直接submit提交,而是js使用$.post()提交的,表单的信息使用serialize()序列化之后传参的。这个操作会引发一个问题。。。后面再说。。。
在webapp/WEB-INF下新建一个pic文件夹用来存放上传的图片:
controller:
这里选择用添加新用户时上传头像图片例子
1 | "addUser",method = RequestMethod.POST) (value = |
MultipartFile这个类一般用来接受前台传过来的文件,提供了一些比较便捷的方法。
接下来完善service和dao,直接insert没什么特色就不细说了。
可以在application.properties中加入上传大小限制的配置:
1 | 100MB = |
出现问题:
完善上面的编码之后,运行发现添加请求之后处理器方法有一个空指针异常。。fileUpload是null。。。经过排查觉得应该是serialize(),因为上传文件的文件流不能被序列化并传递导致后台接收不到MultipartFile对象。
经过思考和查询,总结了解决这个问题大概有如下几种方法:
逃避这个问题:serialize()使用的情况比较局限,只能正确序列化check、text、password等等简单的常用类型。不用异步请求。。。就不用serialize()去序列化,直接submit提交form表单。(废话。。。)
使用异步请求:需要使用FormData对象封装form表单。
FormData 对象可以把form中所有表单元素的name与value组成一个queryString,提交到后台,在使用Ajax提交时,使用FormData对象可以减少拼接queryString的工作量。
1
2
3var formdata=new FromData($("#form")[0]);
formser.append("test",test);//可增加请求的参数
FormData还有set、get、has等等很多方法formData.append(‘a’, 1)意为添加一个键值对,重复添加的键不会被覆盖。
formData.set(‘a’, 1)意为修改某个键的值,如果不存在则作用等同于append,但是重复添加的值会相互覆盖。
注意:这里的[0]去掉的话会报错!Failed to construct ‘FormData’: parameter 1 is not of type ‘HTMLFormElement’.
这里出现了一个很鬼畜的现象:id选择器,为什么可以取出一个数组???
为什么要加[0]?因为new FormData需要的是一个HtmlElement类型的数据,但是JQ得到的是一个HtmlElement的集合,及时这个集合只有一个元素,也要用[ ]取一个元素。
JQ是一个伪数组对象,虽然其本身是一个对象,但也能表现出数组的特点:有length,能用下标取值。为什么这么设计?
为什么这么设计?因为JS有getElementByXX和getElementsByXX,但是JQ却没有这么的直白,举个例子:
$(“.someClass”) 这个时候将所有匹配到DOM元素对象放在jQuery维护的数组中;
$(“#id”)这个时候将所有匹配到DOM元素对象放在jQuery维护的数组中;
在数组的特征外,jQuery还可以调用next()
,last()
等方法(返回结果也还是jQuery对象,jQuery链式功能),总而言之jQuery得到的是个HTMLElement的集合基础上的封装后的对象。将添加用户的js改为:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20function user_insert() {
var formdata = new FormData($("#addUserForm")[0]);
$.ajax({
type:"POST",
url:"addUser",
data:formdata,
processData:false,
contentType:false,
//async:false,
success:function(data){
if (data=="OK"){
alert("添加成功");
window.location.reload();
}else {
alert("添加失败");
window.location.reload();
}
}
});
}需要注意的几点:
- processData必须设置为false。因为data值是FormData对象,不需要对数据做处理。
- contentType必须设置为false,不设置contentType的值,因为使用form表单构造的FormData对象,且form表单已经声明了属性enctype=”multipart/form-data”,所以这里设置为false。
- async:要求为Boolean类型的参数,默认设置为true,所有请求均为异步请求。如果需要发送同步请求,请将此选项设置为false。注意,同步请求将锁住浏览器,用户其他操作必须等待请求完成才可以执行。
虽然不是什么很高大上的问题,但是想我一样初学者遇到时还是很棘手的。暂时使用这样的方式完成了上传并解决了小麻烦。上面的controller代码是添加用户的,如果是修改用户还需要在jsp页面传一个修改前pic的值,方便对修改前的图片进行删除。代码如下:
1 | "updateUser") ( |