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对象,这里很少解释,能够百度详情
相关文章
相关标签/搜索