SpringMVC学习(九)——SpringMVC中实现文件上传
这一篇博文主要来总结下SpringMVC中实现文件上传的步骤。但这里我只讲单个文件的上传。
环境准备
SpringMVC上传文件的功能需要两个jar包的支持,如下:
工程中肯定要导入以上两个jar包,主要是CommonsMultipartResolver解析器依赖commons-fileupload和commons-io这两个jar包。
单个文件的上传
前台页面
我们要改造editItem.jsp页面,主要是在form表单中添加商品图片一栏,效果我截图如下:
注意一点的是form表单中别忘了写enctype="multipart/form-data"
属性。
对多部件类型multipart解析
意思就是说针对上面的enctype=”multipart/form-data”类型,SpringMVC需要对multipart类型的数据进行解析,在springmvc.xml中配置multipart类型解析器即可。如下:
<!-- 配置多媒体文件解析器 -->
<!-- 文件上传 -->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 设置上传文件的最大尺寸为5MB -->
<property name="maxUploadSize">
<value>5242880</value>
</property>
</bean>
创建文件保存的虚拟目录
在上传文件之前,首先要创建一个虚拟目录来保存文件,这个虚拟目录会对应磁盘上的一个实际的目录,在实际开发中肯定会有一个服务器专门存储资源的,在这里我们就用本地磁盘来保存文件,然后映射一个虚拟目录,用来在程序中指定获取文件的路径(其实上面前台页面editItem.jsp中,那个src=”/pic/${item.pic}”中的/pic就是虚拟目录)。
创建的方法有两种:一是在Eclipse中双击tomcat服务器,就会弹出下面的框框:
选择Modules,然后点击【Add External Web Module…】,在弹出的对话框中进行如下操作,都在图里面了。
设置好后,保存即可,这样上传的文件都会保存到Document base指定的目录中,相当于虚拟映射到Path指定的目录中,程序中获取这个文件,要从Path指定的虚拟目录中获取,即我上面的/pic。我可举一个例子,将一个图片(例如,0d318effd664668f555d43966b3294a6_b.jpg)拷贝到Document base指定的目录中,即F:\temp\images目录中,然后启动tomcat服务器,在浏览器地址栏中输入url访问地址——http://localhost:8080/pic/0d318effd664668f555d43966b3294a6_b.jpg
即可访问到该图片。
第二种方法就是在tomcat的配置文件中配置一下,其实刚刚在Eclipse中的操作已经自动写到这个配置文件中了,配置文件位置在tomcat目录/conf/server.xml中,看一下里面会多了一行:
这就是刚刚我配置的,它自动写到这个文件中了,所以我们也可以直接自己在该配置文件中写,就不需要在Eclipse中配置了。
编写后台Controller方法
接下来就是重点了,前台传过来的文件,我们在ItemController类中需要进行处理,然后保存到磁盘中,同时也就映射到了我们配置的虚拟路径中了,那么如何接收呢?看下面的代码:
@RequestMapping(value="/updateitem",method={RequestMethod.POST,RequestMethod.GET}) public String updateItems(Items items, MultipartFile picture) throws Exception { // 把图片保存到图片目录下 // 保存图片,这个图片有的时候文件名可能会重复,你保存多了会把原来的图片给覆盖掉,这就不太合适了。 // 所以为每个文件生成一个新的文件名 String picName = UUID.randomUUID().toString(); // 截取文件的扩展名(如.jpg) String oriName = picture.getOriginalFilename(); String extName = oriName.substring(oriName.lastIndexOf(".")); // 保存文件 picture.transferTo(new File("F:\\temp\\images\\" + picName + extName)); // 把文件名保存到数据库 items.setPic(picName + extName); itemService.updateItem(items); return "forward:/item/itemEdit.action"; }
注意:考虑实际情况,保存图片时一般不只你一个人保存图片,那么这个图片有的时候文件名可能会重复,你保存多了会把原来的图片给覆盖掉,这就不太合适了,所以需要使用UUID算法为每个文件生成一个新的文件名。
首先来看一下形参,主要有Items和MultipartFile类型的picture,我这里上传一张图片是ItemsCustom类的一个属性,所以有了这个形参,是为了写到该类中,SpringMVC文件上传的类是MultipartFile,参数名称picture必须和前台editItem.jsp页面的name属性一致才行。上传图片之后的效果类似于:
并且图片的文件名也保存到了数据库表中,如下:
最后总结一下,我上传文件的逻辑可能判断不严谨,如果说要更加严谨一点的话, 上传文件的逻辑就应是:首先判断有没有上传文件,如果上传了,那么对文件重新命名然后写到磁盘中。如果没有上传文件,那么我应该还是用原来的文件(图片)。读者如果有兴趣可以参考下面代码:
@RequestMapping(value="/updateitem",method={RequestMethod.POST,RequestMethod.GET}) public String updateItems(Items items, MultipartFile picture) throws Exception { // 处理上传的单个图片 String originalFileName = picture.getOriginalFilename();// 原始名称 // 上传图片 if (picture != null && originalFileName != null && originalFileName.length() > 0) { // 存储图片的物理路径,实际中是要写到配置文件中的,不能在这写死 String pic_path = "F:\\temp\\images\\"; // 新的图片名称 String newFileName = UUID.randomUUID() + originalFileName.substring(originalFileName .lastIndexOf(".")); File newFile = new File(pic_path + newFileName);//新图片 picture.transferTo(newFile);// 将内存中的数据写入磁盘 items.setPic(newFileName);// 将新图片名称写到itemsCustom中 } else { //如果用户没有选择图片就上传了,还用原来的图片 Items temp = itemsService.findItemsById(items.getId()); items.setPic(temp.getPic()); } // 调用service更新商品信息,页面需要将商品信息传到此方法 itemService.updateItem(items); return "forward:/item/itemEdit.action"; }
到这里,单个文件的上传我就已总结完了。