前言:項目需求中涉及到多圖片上傳的註冊功能,項目中前端使用的是比較老的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對象,這裏很少解釋,能夠百度詳情