用springMVC来做项目,如果遇到文件上传,那么一定要用spring自带的文件处理类来处理上传的文件,因为效率实在高过其他的。
从界面传过来的参数,如果设置了值对象,那么可以从值对象里面取出字符串类型的普通参数,如果不这样做,也可以直接从request里面获得,两种方法都可以。
问题是如果值对象里面写了其他类型的变量,妄想像Struts2那样处理,springMVC就会报出400 Bad Request的错误。
在Struts2里面,我们可以定义一个值对象为
public class ImageVo { private String roomnumber; private File image1 ; private File image2 ; private File image3 ; }
然后在后台从前台传过来的值对象里面取出image1……,那就是一个文件流了,非常好处理上传文件。但springMVC只能接受全部为string类型的字符串值,那么该怎么做呢?
前台Extjs4代码:
/** * 图片添加 * @type {Ext.form.Panel} */ var addImageForm = new Ext.form.Panel({ border: false, bodyPadding: 5, id: 'addImageForm',height:350, fieldDefaults: {labelWidth: 80, labelSeparator: ": ", anchor: '95%'}, items: [ {xtype:'fieldset',title:'图片上传', collapsible:true, items:[ { xtype:'textfield', fieldLabel: '房间号码', name: 'roomnumber', id: 'roomnumber1', allowBlank: false, maskRe: /[\d]/, reegx: /[\d{4}]/, minLength: 4, maxLength: 4, emptyText: '请输入四位的房间号码(前两位代表楼层,后两位代表房间号)', regexText: '请输入正确的房间号码', //验证该房间号码是否存在 ! listeners: { blur: function (e, t, eOpts) { var roomnumber = e.rawValue; if(roomnumber.length == 4){ Ext.Ajax.request({ method: 'post', params: {roomnumber: roomnumber}, url: '/room/findroomhold', callback: function (options, success, response) { var jsonString = Ext.JSON.decode(response.responseText); if (jsonString.success) { } else { Ext.Msg.alert('警告', jsonString.msg); } } }); } } } },{ xtype:'filefield', fieldLabel:'上传图片1', name:'image1', id:'image1', buttonText:'', buttonConfig:{iconCls:'upload'}, listeners:{ change:function(btn, value, eOpts){ var img_reg = /\.([jJ][pP][gG]){1}$|\.([jJ][pP][eE][gG]){1}$|\.([gG][iI][fF]){1}$|\.([pP][nN][gG]){1}$|\.([bB][mM][pP]){1}$/; if ( img_reg.test(value) ) { var img = Ext.getCmp('img1'); var file = btn.fileInputEl.dom.files[0]; var url = URL.createObjectURL(file); img.setSrc(url); } else { Ext.Msg.alert('提示', '请选择图片类型的文件!'); return ; } } } },{ xtype:'filefield', fieldLabel:'上传图片2', name:'image2', id:'image2', buttonText:'', buttonConfig:{iconCls:'upload'}, listeners:{ change:function(btn, value){ var img_reg = /\.([jJ][pP][gG]){1}$|\.([jJ][pP][eE][gG]){1}$|\.([gG][iI][fF]){1}$|\.([pP][nN][gG]){1}$|\.([bB][mM][pP]){1}$/; if ( img_reg.test(value) ) { var img = Ext.getCmp('img2'); var file = btn.fileInputEl.dom.files[0]; var url = URL.createObjectURL(file); img.setSrc(url); } else { Ext.Msg.alert('提示', '请选择图片类型的文件!'); return ; } } } },{ xtype:'filefield', fieldLabel:'上传图片3', name:'image3', id:'image3', buttonText:'', buttonConfig:{iconCls:'upload'}, listeners:{ change:function(btn, value){ var img_reg = /\.([jJ][pP][gG]){1}$|\.([jJ][pP][eE][gG]){1}$|\.([gG][iI][fF]){1}$|\.([pP][nN][gG]){1}$|\.([bB][mM][pP]){1}$/; if ( img_reg.test(value) ) { var img = Ext.getCmp('img3'); var file = btn.fileInputEl.dom.files[0]; var url = URL.createObjectURL(file); img.setSrc(url); } else { Ext.Msg.alert('提示', '请选择图片类型的文件!'); return ; } } } } ] },{xtype:'fieldset',title:'图片预览',layout:'column',defaults:{width:130}, items:[ {xtype:'image',id:'img1'}, {xtype:'image',id:'img2'}, {xtype:'image',id:'img3'} ] } ], dockedItems: [ { xtype: 'toolbar', dock: 'bottom', ui: 'footer', layout: {pack: 'center'}, items: [ {text: '确认上传', disabled: true, formBind: true, handler: function () { var form = this.up('form').getForm(); if (form.isValid()) { form.submit({ url: '/image/add', method: 'post', submitEmptyText: false, waitMsg: '请稍等,系统正在帮您添加', success: function (form, action) { //Ext.Msg.alert('成功', "上传成功!"); Ext.Msg.alert('成功', action.result.msg); }, failure: function (form, action) { Ext.Msg.alert('失败', action.result.msg); //Ext.Msg.alert('失败', "上传失败"); } }) } }}, {text: '重置', handler: function () { this.up('form').getForm().reset(); }} ] } ] });
如图:
后台的java代码,因为只有一个非文件类的值,所以直接放到了参数里面了。
import com.lhx.hotel.model.Image; import com.lhx.hotel.service.ImageService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartHttpServletRequest; import org.springframework.web.multipart.commons.CommonsMultipartResolver; import javax.servlet.http.HttpServletRequest; import java.io.File; import java.io.IOException; import java.util.*; /** * Created by xin on 14-5-18. */ @Controller @RequestMapping("/image") public class ImageController { Map<String,Object> outmap = new HashMap<String, Object>(); @Autowired private ImageService imageService ; @RequestMapping("/add") @ResponseBody public Map<String,Object> add(String roomnumber, HttpServletRequest request) throws IOException { List<String> fileTypes = new ArrayList<String>(); fileTypes.add(".jpg"); fileTypes.add(".jpeg"); fileTypes.add(".bmp"); fileTypes.add(".gif"); fileTypes.add(".png"); //是否成功上传了文件 boolean ishasuploadimage = false ; //成功上传了n个图像 int imageNum = 0 ; /** * 想通过ImageVo imageVo,但需要全部是string类型 * 并且没有其他什么参数,大材小用,舍弃 * String roomnumber = imageVo.getRoomnumber(); */ String imagepath = request.getRealPath("/upload"); CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(request.getSession().getServletContext()); if (multipartResolver.isMultipart(request)){ MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest) request ; //String roomnumber = multiRequest.getParameter("roomnumber"); if (!"".equals(roomnumber) && roomnumber != null){ int ronumber = Integer.valueOf(roomnumber) ; Iterator<String> iter = multiRequest.getFileNames(); Image image = null ; while (iter.hasNext()){ MultipartFile file = multiRequest.getFile((String)iter.next()); //byte[] bytes = file.getBytes(); //long size = file.getSize(); //有上传文件的话,容量是大于0的。 if (file.getSize() > 0){ ishasuploadimage = true ; String imagename = file.getOriginalFilename(); String ext = imagename.substring(imagename.lastIndexOf(".")) ; if (fileTypes.contains(ext)){ //文件名为:房间号码 + image + 系统时间 + 后缀 String fileName = roomnumber + "image" + System.currentTimeMillis() + ext ; image = new Image(); image.setPhpath(fileName); image.setPhroomnumber(ronumber); File localFile = new File(imagepath,fileName); try { file.transferTo(localFile); imageService.insertSelective(image); imageNum ++ ; } catch (IOException e) { e.printStackTrace(); outmap.put("success",false); outmap.put("msg","系统出错"); return outmap ; } } else { outmap.put("success",false); outmap.put("msg","成功上传" + imageNum + "张图片,遇到不能上传的非图片类的文件而出错!"); return outmap ; } } } } else { //这种情况很少见,除非直接在浏览器里面输入地址 outmap.put("success",false); outmap.put("msg","没有对应的房间号"); return outmap ; } } if (!ishasuploadimage){ outmap.put("success",false); outmap.put("msg","没有上传文件"); return outmap ; } outmap.put("success",true); outmap.put("msg","成功上传" + imageNum + "张图片!"); return outmap ; } }
这些代码是先写的,没什么注释,如果不是很看得懂,下面这个例子就详细点了
Extjs4前台代码:
/** * Created by xin on 14-5-19. */ var addStaffForm = new Ext.form.Panel({ border: false, bodyPadding: 5, id: 'addStaffForm', height: 560, fieldDefaults: {labelWidth: 80, labelSeparator: ": ", anchor: '0', margin:'2 2 2 2'}, items:[ { xtype:'fieldset', defaultType: 'textfield', items: [ { xtype: 'container', anchor: '-5', layout: 'column', items: [ {xtype: 'textfield', width: 180, fieldLabel: "姓名", id: 'staffname', name: 'staffname', allowBlank: false, blankText: '姓名必须输入', emptyText: '请输入真实姓名'}, {xtype: 'combobox', width: 180, fieldLabel: "性别", id: 'staffgender', name: 'staffgender', store: sexStore, valueField: 'text', value: '男', allowBlank: false, blankText: '请选择', labelAlign:'right',editable:false, listConfig: { getInnerTpl: function () { return "<img src='public/images/{id}.gif'/>{text}" } }} ] }, { xtype: 'container', anchor: '-5', layout: 'column', items: [ {xtype: 'textfield', fieldLabel: "职责", width: 180, id: 'staffprofession', name: 'staffprofession', emptyText: '请输入职责名称', allowBlank: false, blankText: '职责名称必须输入'}, {xtype: 'textfield', fieldLabel: "部门", width: 180, id: 'staffdepartment', name: 'staffdepartment', allowBlank: false,labelAlign:'right'} ] }, { xtype:'numberfield', fieldLabel: "月薪", id: 'staffsalary', name: 'staffsalary', anchor: '95%', step:50, maxValue:999999, allowBlank: true, minValue:0, negativeText:'月薪不能为负数', emptyText: '请填入月薪' }, { xtype: 'fieldcontainer', fieldLabel: '电话', defaultType: 'textfield', layout: 'hbox', anchor: '95%', combineErrors: true, items: [ {name: 'staffzone', id: 'staffzone', width: 50, emptyText: '区号', maskRe: /[\d]/, reegx: /[\d{3,4}]/, minLength: 3, maxLength: 4, regexText: '请输入正确的区号'}, {xtype: 'label', text: '--'}, {name: 'stafftelnumber', id: 'stafftelnumber', flex: 1, emptyText: '请输入电话号码', maskRe: /[\d]/, reegx: /[\d{7,8}]/, minLength: 7, maxLength: 8, regexText: '请输入正确的电话号码'} ] }, { fieldLabel: '手机号码', name: 'staffmobile', id: 'staffmobile', allowBlank: true, anchor: '95%', maskRe: /[\d]/, reegx: /[\d{11}]/, minLength: 11, maxLength: 11, emptyText: '请输入手机号码', regexText: '请输入正确的手机号码' }, { fieldLabel: '电子邮箱', name: 'staffemail', id: 'staffemail', allowBlank: true, anchor: '95%', emptyText: '请输入电子邮箱地址', vtype: 'email' }, { xtype: 'fieldcontainer', fieldLabel: '地区', defaultType: 'combobox', layout: 'hbox', anchor: '95%', items: [ {name: 'staffprovince', id: 'staffprovince', width: 90, emptyText: '请选择省', valueField: 'regionId', displayField: 'regionName', store: provinceStore, queryMode: 'local', triggerAction: 'all', selectOnFocus: true, forceSelection: true,editable:false, listeners: { change: function (f, newValue, oldValue) { var cityStore = Ext.data.StoreManager.lookup('cityStore'); var city = this.up('form').getForm().findField('staffcity'); var county = this.up('form').getForm().findField('staffcounty'); if (newValue && newValue != oldValue) { city.setValue(); county.setValue(); cityStore.load({params: {parentId: newValue}}); } } } }, {name: 'staffcity', id: 'staffcity', width: 90, store: cityStore, emptyText: '请选择市', valueField: 'regionId', displayField: 'regionName',queryMode: 'local', triggerAction: 'all', selectOnFocus: true, forceSelection: true, editable:false, listeners: { change: function (f, newValue, oldValue) { var countyStore = Ext.data.StoreManager.lookup('countyStore'); var county = this.up('form').getForm().findField('staffcounty'); if (newValue && newValue != oldValue) { county.setValue(); countyStore.load({params: {parentId: newValue}}); } } } }, {name: 'staffcounty', id: 'staffcounty', width: 90, store: countyStore, emptyText: '请选择区(县)', valueField: 'regionId', displayField: 'regionName',queryMode: 'local', triggerAction: 'all', selectOnFocus: true, editable:false, forceSelection: true} ] }, { fieldLabel: '地址', name: 'staffpartaddress', id: 'staffpartaddress', allowBlank: true, emptyText: '请输入详细地址', anchor: '95%' }, { xtype:'filefield', fieldLabel:'上传头像', name:'avatar', id:'avatar', anchor: '95%', buttonText:'', buttonConfig:{iconCls:'upload'}, listeners:{ change:function(btn,value){ //是否是规定的图片类型 var img_reg = /\.([jJ][pP][gG]){1}$|\.([jJ][pP][eE][gG]){1}$|\.([gG][iI][fF]){1}$|\.([pP][nN][gG]){1}$|\.([bB][mM][pP]){1}$/; if (img_reg.test(value)) { var img = Ext.getCmp('staffavatar'); var file = btn.fileInputEl.dom.files[0]; var url = URL.createObjectURL(file); img.setSrc(url); } else { Ext.Msg.alert('提示', '请选择图片类型的文件!'); return ; } } } } ] }, { xtype: 'fieldset', title: '图片预览', defaults: {margin:'1 1 1 100', width: 200,height:260}, items: [ {xtype: 'image', id: 'staffavatar',border: 2, style: { borderColor: 'blue', borderStyle: 'solid' }} ] } ], dockedItems: [ { xtype: 'toolbar', dock: 'bottom', ui: 'footer', layout: {pack: 'center'}, items: [ {text: '添加', disabled: true, formBind: true, handler: function () { var form = this.up('form').getForm(); if (form.isValid()) { form.submit({ url: '/staff/add', method: 'post', submitEmptyText: false, waitMsg: '请稍等,系统正在帮您添加', success: function (form, action) { Ext.Msg.alert('成功', action.result.msg); //清除填过的内容 //form.findField('guestname').setValue(); }, failure: function (form, action) { Ext.Msg.alert('失败', action.result.msg); } }) } }}, {text: '重置', handler: function () { this.up('form').getForm().reset(); }} ] } ] });
显示效果:
写一个值对象:
/** * Created by xin on 14-5-20. */ public class StaffVo { private String staffname ; private String staffgender ; private String staffprofession ; private String staffdepartment ; // 部门 //如果改为double类型,这个上传的值不能为空值,所有改为string类型 private String staffsalary ; //月薪 private String staffzone ; //区号 private String stafftelnumber ; //号码 private String staffmobile ; //手机号码 private String staffemail ; //邮箱 private String staffprovince ; private String staffcity ; private String staffcounty ; private String staffpartaddress ; /** * 这个file类型在这里是有问题的,不象struts可进行封装,如果有上传文件, * 就不能整个对象传过去了,所有要进行删除 */ //private File avatar ; //头像 public String getStaffname() { return staffname; } public void setStaffname(String staffname) { this.staffname = staffname; } public String getStaffgender() { return staffgender; } public void setStaffgender(String staffgender) { this.staffgender = staffgender; } public String getStaffprofession() { return staffprofession; } public void setStaffprofession(String staffprofession) { this.staffprofession = staffprofession; } public String getStaffdepartment() { return staffdepartment; } public void setStaffdepartment(String staffdepartment) { this.staffdepartment = staffdepartment; } public String getStaffsalary() { return staffsalary; } public void setStaffsalary(String staffsalary) { this.staffsalary = staffsalary; } public String getStaffzone() { return staffzone; } public void setStaffzone(String staffzone) { this.staffzone = staffzone; } public String getStafftelnumber() { return stafftelnumber; } public void setStafftelnumber(String stafftelnumber) { this.stafftelnumber = stafftelnumber; } public String getStaffmobile() { return staffmobile; } public void setStaffmobile(String staffmobile) { this.staffmobile = staffmobile; } public String getStaffemail() { return staffemail; } public void setStaffemail(String staffemail) { this.staffemail = staffemail; } public String getStaffprovince() { return staffprovince; } public void setStaffprovince(String staffprovince) { this.staffprovince = staffprovince; } public String getStaffcity() { return staffcity; } public void setStaffcity(String staffcity) { this.staffcity = staffcity; } public String getStaffcounty() { return staffcounty; } public void setStaffcounty(String staffcounty) { this.staffcounty = staffcounty; } public String getStaffpartaddress() { return staffpartaddress; } public void setStaffpartaddress(String staffpartaddress) { this.staffpartaddress = staffpartaddress; } }
数据库的持久层对象就不写了,大同小异。处理的java类
import com.lhx.hotel.model.Staff; import com.lhx.hotel.service.StaffService; import com.lhx.hotel.vo.StaffVo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartHttpServletRequest; import org.springframework.web.multipart.commons.CommonsMultipartResolver; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.io.File; import java.io.IOException; import java.util.*; /** * Created by xin on 14-5-20. */ @Controller @RequestMapping("/staff") public class StaffController { //返回的map,会自动封装成json Map<String,Object> outmap = new HashMap<String, Object>(); @Autowired private StaffService staffService ; @RequestMapping("/add") @ResponseBody public Map<String,Object> add(StaffVo staffVo, HttpServletRequest request, HttpServletResponse response){ //持久层对象 Staff staff = new Staff(); /** * 基本信息的提取 * 因为都是string类型,所有不可能为空, * 持久层对象属性同是string类型的,不用进行判断, * 非string类型的要进行判断,防止异常 * extjs界面如果有格式限制的话,就不用trim处理,因为不允许输入空格 * 没有限制的话还是要进行trim处理的 * 对null进行trim处理会出现空指针异常,但幸亏传过来的值是空值"" */ String staffname = staffVo.getStaffname().trim(); String staffgender = staffVo.getStaffgender(); String staffprofession = staffVo.getStaffprofession().trim(); String staffdepartment = staffVo.getStaffdepartment().trim(); String staffzone = staffVo.getStaffzone(); String stafftelnumber = staffVo.getStafftelnumber(); String telphone = staffzone + stafftelnumber ; String staffmobile = staffVo.getStaffmobile(); String staffemail = staffVo.getStaffemail(); //地址,进行拼接 String staffprovince = staffVo.getStaffprovince(); String staffcity = staffVo.getStaffcity(); String staffcounty = staffVo.getStaffcounty(); String staffpartaddress = staffVo.getStaffpartaddress().trim(); String address = staffprovince + staffcity + staffcounty + staffpartaddress ; //非string类型,要进行判断 String staffsalary = staffVo.getStaffsalary(); if (!"".equals(staffsalary)){ double salary = Double.valueOf(staffsalary); staff.setStsalary(salary); } //后加的工号 年份月份加序号(位数不够前面加0) Calendar c = Calendar.getInstance(); int year = c.get(Calendar.YEAR); int month = c.get(Calendar.MONTH); int staffnum = staffService.getrecordSize() ; String staffnumstr=String.valueOf(staffnum); String [] ss = {"00000","0000","000","00","0",""}; staffnumstr = ss[staffnumstr.length()-1] + staffnumstr; String stjobnumber = year + "" + month + staffnumstr; /** * 填充到持久层对象中 */ staff.setStname(staffname); staff.setStgender(staffgender); staff.setStprofession(staffprofession); staff.setStdepartment(staffdepartment); staff.setSttelphone(telphone); staff.setStmobile(staffmobile); staff.setStemail(staffemail); staff.setStaddress(address); staff.setStjobnumber(stjobnumber); /** * 对上传文件进行处理 */ //获取servlet上下文 ServletContext servletContext = request.getSession().getServletContext(); //spring的文件处理解析类,包装了servlet的上下文。 CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(servletContext); //是否有上传文件 boolean hasUploadAvatar = false ; //如果是multipart的提交,这个判断有点多余,不过还是加上吧! if (multipartResolver.isMultipart(request)) { //把request请求进行升级,request有的,它都有, MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest) request; /** * String staffname = multiRequest.getParameter("staffname"); * 这样也可以获得上面从staffVo里面取出的值 */ //获得上传文件的名称 Iterator<String> iter = multiRequest.getFileNames(); //如果有的话就依次取出来 while (iter.hasNext()){ //包装过的文件流 MultipartFile file = multiRequest.getFile((String)iter.next()); //这里要进行判断,即使是空值,没有上传内容,file都是有值(空值)的,而文件流大小要大于0才是有上传的东西 if (file.getSize() > 0){ //获得上传文件原始名 String imagename = file.getOriginalFilename(); //算出后缀名 String ext = imagename.substring(imagename.lastIndexOf(".")) ; //对文件类型进行判断,这个操作也可以在前台进行处理,在前台进行处理比较好,前后台都进行处理最稳妥 List<String> fileTypes = new ArrayList<String>(); fileTypes.add(".jpg"); fileTypes.add(".jpeg"); fileTypes.add(".bmp"); fileTypes.add(".gif"); fileTypes.add(".png"); //是图片再进行处理 if (fileTypes.contains(ext.toLowerCase())){ //文件名为:唯一的工号 + avatar + 系统时间 + 后缀 String fileName = stjobnumber + "avatar" + System.currentTimeMillis() + ext ; //文件夹;String imagepath = request.getRealPath("/upload");过时的方法,用request.getSession().getServletContext()代替 String avatarFolder = servletContext.getRealPath("/upload"); File localFile = new File(avatarFolder,fileName); try { //直接写入到后台服务器,简单且快 file.transferTo(localFile); staff.setStavatar(fileName); //保存资料到数据库中 staffService.insertSelective(staff); outmap.put("success",true); outmap.put("msg","添加成功!"); return outmap ; } catch (IOException e) { e.printStackTrace(); outmap.put("success",false); outmap.put("msg","系统出错"); return outmap ; } } else { outmap.put("success",false); outmap.put("msg","图片格式出错!"); return outmap ; } } } } if (!hasUploadAvatar){ staffService.insertSelective(staff); outmap.put("success",true); outmap.put("msg","添加成功!"); return outmap ; } return null ; } }
上传其他文件,把代码改一下就可以了……写这文章给自己做一个总结,也给其他人一个学习参考的选择。
自我总结的小技巧:文件上传,新建一个文件夹,比如upload。里面有个文件的话,部署后tomcat那边就有upload这个文件夹了,如果只是空的文件夹,部署可能不会自动生成,到时上传文件就找不到路径了。
上传文件要放入一个文件夹upload中,先前设置springMVC的默认访问路径都加上前缀和后缀,所以如果不记得加入以下这行代码的话,就会造成访问不了新上传的图片!
<!--允许静态资源访问-->
<mvc:resources mapping="/upload/**" location="/upload/" />