FormData上傳多文件+DefaultMultipartHttpServletRequest保存文件

前言:項目需求中涉及到多圖片上傳的註冊功能,項目中前端使用的是比較老的ionic1+angularJS,後端SpringMVC,開發途中的遇到的各類爬坑血淚史,在此分享出來給你們參考。javascript

1.組建表單

因涉及到多圖片上傳以及加載本地圖片,故採用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

<form>:

  • enctype='multipart/form-data',是設置表單的MIME編碼。默認狀況,這個編碼格式是application/x-www-form-urlencoded,不能用於文件上傳;只有使用了multipart/form-data才能將文件以二進制的形式上傳,從而實現多種類型的文件上傳。
  • method="post",若是表單指定請求方式爲post,則在使用XMLHttpRequest對象.open()方法請求後臺時,該請求爲post請求。
  • novalidate ,該屬性規定當提交表單時不對其進行驗證。因爲本項目中用的是angularJS的表單驗證故屏蔽了H5的表單驗證。
  • ng-submit,指令用於在表單提交後執行指定函數

<input>:

  • accept="image/*",H5中容許上傳圖片的類型。
  • ng-pattern,確保輸入匹配一個正則表達式。

2.組裝FormData對象,準備上傳

這裏使用的是 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.
跨域

3.接收表單數據

後端使用的是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對象,這裏很少解釋,能夠百度詳情
相關文章
相關標籤/搜索