表单的文件信息使用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 | (value = "addUser",method = RequestMethod.POST) | 
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
 3- var 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
 20- function 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") |