Introduction: The requirement of the project involves the registration function of multi-image uploading. The front-end of the project uses the old Ionic1 +angularJS and the back-end SpringMVC. The history of various difficulties encountered during the development is shared here for everyone’s reference.

1. Form a form

As it involves multiple image uploads and loading local images, the FormData form object of H5 is used


Pasted some HTML code as an example, the following form uploads two text and three images in the background

<ion-view ng-controller="regController as vm" title="User Registration">    <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">Account:</span>                <input type="text" required placeholder="Suggested cell phone number." ng-model="vm.user.account" name='account'> </label>            <label class="item item-input item-stacked-label">                <span class="input-label">Password:</span>                <input type="password" required ng-pattern="^ \ w [a zA - Z] {5} in 2 $" ng-maxlength="18" ng-minlength="6"                    ng-model="vm.user.password" name='password' placeholder="Must start with a letter and contain only 6 to 18 letters, digits, and underscores (_)."> </label>
            <label class="item item-input item-stacked-label">                <span class="input-label">Name:</span>                <input type="text" required ng-maxlength="6" ng-model="vm.user.name" placeholder="No more than 6 words in length." name='name' ></label>            <div class="item item-input item-stacked-label">                <span class="input-label">Upload photos:</span>                <div class="row">                    <div class="col ">                        <label for="">1. Photos of surroundings<br>                            <a href="javascript:;" class="file">Choose photos<input accept="image/*" type="file" name='img_environment'> </a>                        </label>                    </div>                </div>                <div class="row">                    <div class="col">                        <label for="">2. Photos of the reverse installation area<br>                            <a href="javascript:;" class="file">Choose photos<input accept="image/*" type="file" name='img_area'> </a>                        </label>                    </div>                </div>                <div class="row">                    <div class="col">                        <label for="">3. Base and counterweight diagram<br>                            <a href="javascript:;" class="file">Choose photos<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">complete</button>                </div>            </div>        </form>    </ion-content></ion-view>

Copy the code

The important thing to note here are all

tags and
tags. I’ll walk through the attributes of each tag in the following sections

<form>:

  • Enctype =’multipart/form-data’, which sets the MIME encoding of the form. By default, this encoding format is Application/X-www-form-urlencoded and cannot be used for file uploads; Files can be uploaded in binary format only when multipart/form-data is used, enabling multiple file types to be uploaded.
  • Method =”post”, if the form specifies a POST request, the request is a POST request when the background is requested using the XMLHttpRequest object.open() method.
  • Novalidate, which specifies that the form is not validated when it is submitted. This project uses angularJS form validation and excludes H5 form validation.
  • The ng-submit directive is used to execute the specified function after the form is submitted

The < input > :

  • Accept =”image/*”, H5 allows the type of image to be uploaded.
  • Ng-pattern, ensuring that the input matches a regular expression.

2. Assemble the FormData object and prepare to upload it

This is what we’re using here
Ng – submit method
save(userForm)
Pass in the form object for form validation

Let’s paste the js code of the Controller layer,

function save(userForm){            if(userForm.$valid){If (vm.user.img[0]! If (vm.user.img[0]. Size ===3){var formData = new formData (document.getelementById ('userForm')); Var countImgSize = get the form object on the page formdata.get('img_environment').size+formdata.get('img_area').size+formdata.get('img_basics').size; If (countImgSize<1024*1024*10){// If the total size of three images is less than 10MB console.log(' Form verification succeeded, preparing to upload user information '); formdata.set('name',encodeURI(formdata.get('name'))); Function (data){if (data.data.state=="success") {$timeout(function () {$timeout(function () {$timeout(function ()){ $ionicPopup.alert({title: 'status ', template: data.data.mes}); }, 500).then(function () { $state.go('login'); }); } else if(data.data.state=="faild"){$ionicPopup. Alert ({title: 'register status ', template: data.data.mes }).then(function (res) { }); }})}else{$ionicpopup. alert({title: 'warning ', template: +(countImgSize/1024/1024).tofixed (2)+'MB'}); }}else{$ionicpopup. alert({title: 'image not uploaded'); }}else{$ionicpopup. alert({title: 'please upload image'}); }}else{if(userForm.account.$error. Required){$ionicPopup.alert({title: 'account is not filled'}); } if(userform.password.$error. Required){$ionicPopup.alert({title: 'password is not filled'}); } if(userform.password.$error. Pattern){$ionicpopup. alert({title: 'password is invalid'}); } if(userform.password. $error.maxlength){$ionicPopup.alert({title: 'Password exceeds maximum length'}); } if(userform.password.$error. Minlength){$ionicPopup.alert({title: 'password is less than minimum length'}); }
                if(userForm.name.$error.maxlength){// If the name exceeds the maximum length $ionicpopup. alert({title: 'Name exceeds the maximum length'}); } if(userform.name.$error. Required){// If $ionicPopup.alert({title: 'left blank'}); }}}Copy the code

The above code focuses on lines 5-10, but line 8 is the focus:

Because forms use multipart/form-data encoding,

The request header contains content-type: multipart/form-data. boundary=—-WebKitFormBoundaryW8oohHhEVIBMNJp9

The content-type of the request header is multipart/form-data. The content-type of the request header is multipart/form-data. Utf8, but since the POST request uses angularJS $HTTP, (encodeURI (STR) is used to transcode all Chinese formdata in formdata, and urldecoder.decode (STR, “Utf-8 “) (note :”UTF-8” should be capitalized)

The Service layer is simple, using the $HTTP service to request the background,post the request, and pass in the form object

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);
    }Copy the code

The important thing here is to make the current request a Multipart/form-data request, since the files are uploaded via an AnjularJS HTTP request. Anjularjs’s default Content-type header for POST and GET requests is Application /json. By setting ‘content-type’ : undefined, the browser not only helps us set the content-type to multipart/form-data, but also fills in the current boundary. If you manually set it to ‘content-type’ : Multipart /form-data: The current request boundary parameter is null. The anjularJS transformRequest function serializes our formData Object by setting transformRequest: angular.identity.

3. Receive form data

The back end uses springMVC, which is described in the code comments

@RequestMapping(value = "/regUser", method = RequestMethod.POST)
@ResponseBody
public JSONObject addUser(HttpServletRequest request) throws IOException {
    / / instantiate a first file parser (the moment called parser, DefaultMultipartHttpServletRequest translation named 'multiple requests by default)
    DefaultMultipartHttpServletRequest dmhsq = (DefaultMultipartHttpServletRequest) request;
    // This userOrder is the user object that I need to assemble and persist
    UserOrder userOrder = new UserOrder();
    The following two lines of code cover all data in all forms
    Map map = dmhsq.getParameterMap();
    // Get all files
    Map<String, MultipartFile> fileMap = dmhsq.getFileMap();
    // Upload the image
    //1. Image storage path
    StringBuffer pre_save_path= new StringBuffer();
    String path = System.getProperty("java.class.path");
    path = path.substring(0, path.lastIndexOf("jboss-modules.jar"));// The root of the application server
    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       // Separate the images to be saved with commas
        pre_save_path.append("/upload/" + ((String[]) map.get("orderId"))0] + "/" + originalFilename+",");
        //4. Save the image folder
        File folder = new File(path + "upload" + "/" + ((String[]) map.get("orderId"))0] + "/");
        // create a folder to see if it exists
        if(! folder.isDirectory()) { folder.mkdirs(); }//6. Throw in the new image
        m.transferTo(newfile);
    }
    // The following is some business logic needs to pay attention to the Chinese decoding of the piece
    userOrder.setVerifyPicture(pre_save_path.toString().substring(0,pre_save_path.toString().length()-1));// Verify the image
    // It is time to upload the registration information
    // Use urldecoder.decode (STR, "UTF-8") to decode the backend.
    String name = ((String[]) map.get("name"))0];
    name = URLDecoder.decode(name, "UTF-8");
    userOrder.setName(name);/ / name

    userOrder.setUserAccount(((String[]) map.get("account"))0]);/ / account
    userOrder.setPassword(((String[]) map.get("password"))0]);/ / password
    // Get the userOrder object for the current order number
    UserOrder u = userOrderService.findUserOrder(userOrder.getOrderId());
    // Check whether the account exists
    Boolean isExist = userService.hasAccountExist(userOrder.getUserAccount());
    JSONObject returnObj = new JSONObject();
    // Create a new userOrder
    if (u == null && !isExist) {
        userOrderService.createUserOrder(userOrder);
        returnObj.put("mes"."Registered successfully, you will be redirected to the login page ~");
        returnObj.put("state"."success");
    } else {
        returnObj.put("mes"."Registration failed, current order already registered or account already exists!");
        returnObj.put("state"."faild");
    }

    return returnObj;
}Copy the code

Here DefaultMultipartHttpServletRequest file parser is a good thing, can get through it all on the form of text data and MultipartFile object, there is not much to explain, to baidu for details