前言:项目需求中涉及到多图片上传的注册功能,项目中前端使用的是比较老的ionic1+angularJS,后端SpringMVC,开发途中的遇到的各类爬坑血泪史,在此分享出来给你们参考。javascript
因涉及到多图片上传以及加载本地图片,故采用H5的FormData表单对象
这里先粘出部分HTML代码做为示例,如下表单上传到后台的数据有两个文本以及三张图片前端
<ion-view ng-controller="regController as vm" title="用户注册"> <ion-content class="content-bg-color" has-supheader="false" overflow-scroll="true"> <form enctype="multipart/form-data" method="post" novalidate ng-submit="vm.save(userForm)" name='userForm' id='userForm'> <label class="item item-input item-stacked-label"> <span class="input-label">帐号:</span> <input type="text" required placeholder="建议使用手机号" ng-model="vm.user.account" name='account'> </label> <label class="item item-input item-stacked-label"> <span class="input-label">密码:</span> <input type="password" required ng-pattern="^[a-zA-Z]\w{5,17}$" ng-maxlength="18" ng-minlength="6" ng-model="vm.user.password" name='password' placeholder="以字母开头,长度在6~18之间,只能包含字母、数字和下划线"> </label>
<label class="item item-input item-stacked-label"> <span class="input-label">姓名:</span> <input type="text" required ng-maxlength="6" ng-model="vm.user.name" placeholder="长度不得超过6个字" name='name' ></label> <div class="item item-input item-stacked-label"> <span class="input-label">上传照片:</span> <div class="row"> <div class="col "> <label for="">1.周围环境照片 <br> <a href="javascript:;" class="file">选择照片 <input accept="image/*" type="file" name='img_environment'> </a> </label> </div> </div> <div class="row"> <div class="col"> <label for="">2.逆安装区域照片 <br> <a href="javascript:;" class="file">选择照片 <input accept="image/*" type="file" name='img_area'> </a> </label> </div> </div> <div class="row"> <div class="col"> <label for="">3.基础及配重图 <br> <a href="javascript:;" class="file">选择照片 <input accept="image/*" type="file" name='img_basics'> </a> </label> </div> </div> </div> <div class="row"> <div class="col"> <button class="button button-block button-positive" type="submit">完成</button> </div> </div> </form> </ion-content></ion-view>
复制代码
这里须要留意的是全部<form>标签以及<input>标签,如下我会逐步讲解每一个标签的属性java
这里使用的是 ng-submit方法 save(userForm) 传入表单对象进行表单验证
下面仍是先粘出controller层的js代码,angularjs
function save(userForm){ if(userForm.$valid){//成功为 true 不然为 false if(vm.user.img[0]!=undefined){ if(vm.user.img[0].size===3){//若是三张图片都上传了 var formdata = new FormData(document.getElementById('userForm'));//获取页面上的表单对象 var countImgSize = formdata.get('img_environment').size+formdata.get('img_area').size+formdata.get('img_basics').size; if(countImgSize<1024*1024*10){//若是三张图片总大小小于10mb console.log('表单验证成功,正在准备上传用户信息'); formdata.set('name',encodeURI(formdata.get('name')));//转码 regService.reg(formdata).then(function(data){ if (data.data.state=="success") { $timeout(function () { $ionicPopup.alert({ title: '注册状态', template: data.data.mes }); }, 500).then(function () { $state.go('login'); }); } else if(data.data.state=="faild"){ $ionicPopup.alert({ title: '注册状态', template: data.data.mes }).then(function (res) { }); } }) }else{ $ionicPopup.alert({ title: '警告', template: '图片总大小不能超过10MB,当前图片总大小为:'+(countImgSize/1024/1024).toFixed(2)+'MB' }); } }else{ $ionicPopup.alert({ title: '图片没有上传完' }); } }else{ $ionicPopup.alert({ title: '请上传图片' }); } }else{ if(userForm.account.$error.required){ $ionicPopup.alert({ title: '帐号没填' }); } if(userForm.password.$error.required){ $ionicPopup.alert({ title: '密码没填' }); } if(userForm.password.$error.pattern){ $ionicPopup.alert({ title: '密码不符合填写规则' }); } if(userForm.password.$error.maxlength){ $ionicPopup.alert({ title: '密码超出最大长度' }); } if(userForm.password.$error.minlength){ $ionicPopup.alert({ title: '密码小于最小长度' }); }
if(userForm.name.$error.maxlength){//若是姓名超出最大长度 $ionicPopup.alert({ title: '姓名超出最大长度' }); } if(userForm.name.$error.required){//若是姓名没填 $ionicPopup.alert({ title: '用姓名没填' }); } } }复制代码
以上代码重点在第5-10行,这里主要详细讲解第8行:正则表达式
由于表单使用multipart/form-data编码,spring
请求头里面内容类型为Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryW8oohHhEVIBMNJp9json
不是utf-8格式的,故产生乱码,本身尝试经过改变请求头的Content-Type为multipart/form-data;utf8的方式解决乱码,可是因为post请求使用的是angularjs的$http服务,在尝试强制更改请求头的时候引发了跨域异常,百度各类后无果,因此只能使用如下笨方法:前端使用encodeURI(str)方法将formdata中的全部涉及中文的表单数据转码,后端则使用URLDecoder.decode(str, "UTF-8")的方式解码(注:"UTF-8"应为大写).后端
service层很简单,使用$http服务请求后台,post请求,传入表单对象
function reg(dataform) { return $http.post(SYS_INFO.SERVER_PATH+'/userorder_app/regUser', dataform, { transformRequest: angular.identity, headers: {'Content-Type': undefined} }) .success(complete) .error(failed);
}复制代码
这里要注意的是,由于是经过anjularjs的http请求来上传文件的,因此要让当前的request成为一个Multipart/form-data请求,anjularjs对于post和get请求默认的Content-Type header 是application/json。经过设置‘Content-Type’: undefined,这样浏览器不只帮咱们把Content-Type 设置为 multipart/form-data,还填充上当前的boundary,若是你手动设置为: ‘Content-Type’: multipart/form-data,后台会抛出异常:the current request boundary parameter is null。
ps:
经过设置 transformRequest: angular.identity ,anjularjs transformRequest function 将序列化咱们的formdata object.
跨域
后端使用的是springMVC,详细描述都在代码注释中
@RequestMapping(value = "/regUser", method = RequestMethod.POST)
@ResponseBody
public JSONObject addUser(HttpServletRequest request) throws IOException {
// 先实例化一个文件解析器(暂且先叫解析器,DefaultMultipartHttpServletRequest翻译名叫‘默认的多个请求’)
DefaultMultipartHttpServletRequest dmhsq = (DefaultMultipartHttpServletRequest) request;
//这个userOrder是我须要组装而且要持久化保存的用户对象
UserOrder userOrder = new UserOrder();
//获取全部表单数据 如下两行代码就涵盖了全部表单中的全部数据
Map map = dmhsq.getParameterMap();
//获取全部文件
Map<String, MultipartFile> fileMap = dmhsq.getFileMap();
//上传图片
//1.图片存储的路径
StringBuffer pre_save_path= new StringBuffer();
String path = System.getProperty("java.class.path");
path = path.substring(0, path.lastIndexOf("jboss-modules.jar"));//应用服务器的根路劲
for (String key : fileMap.keySet()) {
MultipartFile m = fileMap.get(key);
//2.图片名称
String originalFilename = new String(m.getOriginalFilename().getBytes("ISO-8859-1"));
//3.新的图片
File newfile = new File(path + "upload" + "/" + ((String[]) map.get("orderId"))[0] + "/" + originalFilename);
a //拼接须要保存的图片路劲以逗号隔开
pre_save_path.append("/upload/" + ((String[]) map.get("orderId"))[0] + "/" + originalFilename+",");
//4.保存图片的文件夹
File folder = new File(path + "upload" + "/" + ((String[]) map.get("orderId"))[0] + "/");
//5.看看文件夹存不存在 不存在就造一个
if (!folder.isDirectory()) {
folder.mkdirs();
}
//6.把新图片扔进去
m.transferTo(newfile);
}
//如下的就是一些业务逻辑 须要注意的是中文解码的那一块
userOrder.setVerifyPicture(pre_save_path.toString().substring(0,pre_save_path.toString().length()-1));//核实图片
//上面图片上传完了 下面该到注册信息了
//后端使用URLDecoder.decode(str, "UTF-8")的方式解码(注:"UTF-8"应为大写)
String name = ((String[]) map.get("name"))[0];
name = URLDecoder.decode(name, "UTF-8");
userOrder.setName(name);//姓名
userOrder.setUserAccount(((String[]) map.get("account"))[0]);//帐号
userOrder.setPassword(((String[]) map.get("password"))[0]);//密码
//获取当前订单号的userorder对象
UserOrder u = userOrderService.findUserOrder(userOrder.getOrderId());
//查看这个帐户存不存在
Boolean isExist = userService.hasAccountExist(userOrder.getUserAccount());
JSONObject returnObj = new JSONObject();
//若是都没有 就新建立一个userorder
if (u == null && !isExist) {
userOrderService.createUserOrder(userOrder);
returnObj.put("mes", "注册成功,即将为您跳转到登录页面~");
returnObj.put("state", "success");
} else {
returnObj.put("mes", "注册失败,当前订单已注册或帐户已存在!");
returnObj.put("state", "faild");
}
return returnObj;
}复制代码
这里的DefaultMultipartHttpServletRequest文件解析器是个好东西,经过它能获取到表单上的全部文本数据和MultipartFile对象,这里很少解释,能够百度详情