SpringMVC之表单提交===③===多文件上传表单
上文简单介绍了springmvc单文件上传表单 ,本文继续介绍多文件上传表单。包含单文件上传的表单已经能够满足大部分功能需求,但任然不够完善。实际业务中可能会包含多个文件同时上传,例如:商家在电商平台申请店铺需要上传身份证扫描文件,这时会有两份上传文件(正/反面)。此时,单文件表单就不能满足需求了;当然你也可以把多个文件拆分为多个表单,关于业务实际问题不在本文讨论范围之内。
在很多时候并不是说问题本身有多难,难的是把问题找出来。只要能把疑问命题找到,距离解决问题也就相差0.1毫米;所以,springmvc 对多文件上传这个问题域有多种解决方案,下面介绍 springmvc 是如何支持多文件上传的!
方案一:使用数组接受表单文件 org.springframework.web.multipart.MultipartFile[]
- 创建多个文件域,当前测试用例表单中文件域数据不固定
<!-- 定义表单结构 --> <div id="normal_form" class="form"><!-- 多文件上传 --> <h2 class="intro">C. 多文件上传表单</h2> <form class="hidden" action="<%=basePath %>form/uploadMore.do" method="post" enctype="multipart/form-data"> <p> <label for="upFile1">选择要上传的文件:</label> <input type="file" name="multipartFiles" /> </p> <p> <input type="button" id="addFileItem" value="Add File Item" /> <input type="submit" value="Submit" /> </p> </form> </div><!-- /多文件上传 -->
- 添加事件
window.console && console.log("form.js"); $(function() { var thisPageFn = new FormNormalFn(); thisPageFn.initEvents(); thisPageFn.initData(); window.console && console.log("Page load event is complete"); }); /** * @description form.jsp 页面交互 * @author Huang.Yong * @version 0.1 * @date 2016年1月5日 - 下午6:16:04 */ function FormNormalFn() { var $thisObj = this; var baseCtt = $("body"); /** * 初始化事件 */ this.initEvents = function() { // 展示隐藏 $(".intro").click(function(){ $(this).parent().find("form").toggleClass("hidden"); }); // 添加上传文件项 $("#addFileItem").click(function(){ var $this = $(this); var btnCtn = $this.parent(); var p =$("<p/>").insertBefore(btnCtn); p.append($("<input/>",{ type : "file", name : "multipartFiles" })).append($("<input/>",{ type : "button", value : " X " }).click(function(){ var $delBtn = $(this); $delBtn.parent().remove(); })); }); } /** * 初始化数据 */ this.initData = function() { } /** * 日志记录 */ function log(msg) { window.console && console.log(msg); } return this; }
- 定义控制器接口
/** * @Title: uploadMore * @Description: 多文件上传 * @param multipartFiles * @return ModelMap */ @RequestMapping("/uploadMore") @ResponseBody public ModelMap uploadMore(@RequestParam("multipartFiles") MultipartFile[] multipartFiles) { boolean flag = false; String message = null; List<ModelMap> data = Lists.newArrayList(); try { for (MultipartFile multipartFile : multipartFiles) { System.out.println(multipartFile.getName() + "-----" + multipartFile.getOriginalFilename()); ModelMap uploadOne = uploadOne(multipartFile); data.add(uploadOne); } flag = true; } catch (Exception e) { flag = false; message = ""; LOGGER.warn(message + " : " + e.getMessage(), e); } return WebUtil.getModelMap(flag, data, message); }
tips:此种方式上传多个文件有两个地方需要特别注意,
①表单类型enctype="multipart/form-data"以及表单名称<input type="file" name="multipartFiles" />;
②控制器参数名称public ModelMap uploadMore(@RequestParam("multipartFiles") MultipartFile[] multipartFiles)
结果验证:
当前选择三个需要上传的文件
断点查看控制器:
由此可见,文件已上传成功!不过此方法有些缺陷:
- 表单文件域名称必须与接口中数组参数名一致
- 只能根据参数 multipartFiles 下标值区分文件,一旦UI中文件域位置调整就会产生严重后果(如:身份证文件与营业执照文件对调等)
方案二:表单实体(FormEntities)+ 请求对象(HttpServletRequest)
- 定义复杂表单
<div id="normal_form" class="form"><!-- 复杂文件上传表单 --> <h2 class="intro">D. 复杂文件上传表单</h2> <form class="hidden" action="<%=basePath %>form/complexForm.do" method="post" enctype="multipart/form-data"> <p> <label for="form4_item1" class="title">文本框:</label> <input type="text" id="form4_item1" class="item" name="formText" /> </p> <p> <label for="form4_item2" class="title">密码框:</label> <input type="password" id="form4_item2" class="item" name="formPwd" /> </p> <p> <label>单选:</label> <input type="radio" id="form1_item3" class="item" name="formRadios" value="rdo1" /> <label for="form1_item3">单选一</label> <input type="radio" id="form1_item32" class="item" name="formRadios" value="rdo2" /> <label for="form1_item32">单选二</label> <input type="radio" id="form1_item33" class="item" name="formRadios" value="rdo3" /> <label for="form1_item33">单选三</label> </p> <p> <label>复选:</label> <input type="checkbox" id="form4_item4" class="item" name="formCheckboxes" value="复选框1" /> <label for="form4_item4" class="title">复选一</label> <input type="checkbox" id="form4_item5" class="item" name="formCheckboxes" value="复选框2" /> <label for="form4_item5" class="title">复选二</label> <input type="checkbox" id="form4_item6" class="item" name="formCheckboxes" value="复选框3" /> <label for="form4_item6" class="title">复选三</label> </p> <p> <label for="form4_sl">下拉:</label> <select name="formSl" id="form1_sl"> <option value="value1">下拉1</option> <option value="value2">下拉2</option> <option value="value3">下拉3</option> <option value="value4">下拉4</option> <option value="value5">下拉5</option> </select> </p> <p> <label for="form4_txta" style="left:left;">多行文本:</label> <textarea name="formTxt" id="form4_txta" cols="30" rows="10" style="resize:none;left:left;"></textarea> </p> <p> <input type="button" class="addFileItem2" value="Add File Item" /> <input type="submit" value="Submit" /> </p> </form> </div><!-- /复杂文件上传表单 -->
- 表单事件,点击【Add File Item】添加一个新的文件域,且文件域name值不能相同
// 添加上传文件项 $(".addFileItem2").click(function(){ var $this = $(this); var btnCtn = $this.parent(); var p =$("<p/>").insertBefore(btnCtn); p.append($("<input/>",{ type : "file", name : "multipartFiles_" + (new Date().getTime()) // 每次不同 })).append($("<input/>",{ type : "button", value : " X " }).click(function(){ var $delBtn = $(this); $delBtn.parent().remove(); })); });
- 开发控制器接口
/** * @Title: complexForm * @Description: 映射复杂表单 * @param form 表单实体映射 * @param request 请求对象 */ @RequestMapping("/complexForm") @ResponseBody public ModelMap complexForm(SimpleForm form, HttpServletRequest request) { boolean flag = false; String message = null; Map<String, Object> data = Maps.newLinkedHashMap(); try { // 表单数据 data.put("formData", form); // 获取附件 // 如果是文件上传: request.getClass() == MultipartHttpServletRequest.class if (request instanceof MultipartHttpServletRequest) { MultipartHttpServletRequest multipartReq = (MultipartHttpServletRequest) request; Map<String, MultipartFile> fileMap = multipartReq.getFileMap(); // 上传到指定位置 if (MapUtils.isNotEmpty(fileMap)) { int count = 1; for (Entry<String, MultipartFile> me : fileMap.entrySet()) { // 表单项名称 String formItemName = me.getKey(); // 与表单项名称关联的唯一附件 MultipartFile multipartFile = me.getValue(); // do something ModelMap uploadOne = this.uploadOne(multipartFile); data.put(formItemName + count, uploadOne); count++; } } } flag = true; } catch (Exception e) { flag = false; message = ""; LOGGER.warn(message + " : " + e.getMessage(), e); } return WebUtil.getModelMap(flag, data, message); }
Tips:使用此种方式上传多个文件附件时,文件域name值不能一致(name作为 Map 的 Key 值);如果可以确定一致性,则推荐使用方案一;
结果验证:
选择需要上传的文件:
断点控制器观察文件列表:
通过结果观察表单其他数据:
至此,关于 springmvc 表单映射已记录完成;其中更多细节部分可以下载附件:ssmFU.zip(maven)查阅研究,欢迎留言探讨学习。