这一讲主要来讲一下SpringMVC是如何实现文件上传的,但请注意这里我只讲单个文件的上传。
环境准备
SpringMVC要想实现上传文件,就得需要两个jar包的支持,如下:
工程中肯定要导入以上两个jar包,因为CommonsMultipartResolver解析器要依赖commons-fileupload和commons-io这两个jar包。
单个文件的上传
前台页面
我们要改造一下itemEdit.jsp页面,主要是在form表单中添加商品图片一栏,效果我截图如下:
注意一点的是form表单中别忘了写enctype="multipart/form-data"
属性。
对多部件类型multipart解析
意思就是说针对上面的enctype="multipart/form-data"
类型,SpringMVC需要对multipart类型的数据进行解析,在springmvc.xml配置文件中配置multipart类型解析器即可。
<!-- 配置多媒体文件解析器 -->
<!-- 注意,这里id必须填写为multipartResolver -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 最大上传文件大小(即上传文件不能超过8M) -->
<property name="maxUploadSize" value="8388608" />
</bean>
创建文件保存的虚拟目录
在上传文件之前,首先要创建一个虚拟目录来保存文件,这个虚拟目录会对应磁盘上的一个实际的目录,在实际开发中肯定会有一个服务器专门存储资源的,但在这里我们就用本地磁盘来保存文件,然后映射一个虚拟目录,用来在程序中指定获取文件的路径(其实上面前台页面itemEdit.jsp中,那个src="/pic/${item.pic}"
中的/pic
就是虚拟目录)。
创建一个虚拟目录来保存文件一共有两种方法:一种方法是在Eclipse中双击Tomcat服务器,就会弹出下面的框框。
选择Modules,然后点击【Add External Web Module…】,在弹出的窗口中进行如下操作,都在图里面了。
设置好之后保存即可,这样上传的文件都会保存到Document base指定的目录中,相当于虚拟映射到Path指定的目录中,程序中获取这个文件,要从Path指定的虚拟目录中获取,即我上面写的/pic
。这里,我举一个例子,比方说将一个图片(例如1.jpg)拷贝到Document base指定的目录中,即F:\temp\images目录中,然后启动Tomcat服务器,在浏览器地址栏中输入http://localhost:8080/pic/1.jpg这样的url访问地址即可访问到该图片。
第二种方法就是在Tomcat服务器的配置文件(即server.xml)中配置一下,其实刚刚在Eclipse中的操作已经自动写到这个配置文件中了,看一下该文件会发现里面会多了一行。
这就是刚刚我配置的,它自动写到server.xml这个文件中了,所以我们也可以直接在该配置文件中进行配置,这样就不需要在Eclipse中配置了。
编写后台Controller方法
接下来就是重点了,前台传过来的文件,我们需要在ItemController类中进行处理,然后保存到磁盘中,同时也就映射到了我们配置的虚拟路径中了,那么如何接收前台传过来的文件呢?看下面的代码:
/**
* 修改商品
* 演示pojo参数绑定。注意,表单里面的元素的name属性要与pojo类中的属性一一对应得上,才能提交过来!
* @param item
* @return
* @throws Exception
*/ //请求方式的限定,不写默认匹配所有请求方式
@RequestMapping(value="updateItem", method={RequestMethod.POST, RequestMethod.GET})
public String updateItem(Item item, MultipartFile pictureFile, Model model) throws Exception {
//保存文件(图片)时,这个文件(图片)有的时候文件名可能会重复,你保存多了会把原来的文件(图片)给覆盖掉,这就不太合适了,所以可以为每个文件(图片)生成一个新的文件名
//新的文件(图片)名字
String newName = UUID.randomUUID().toString();
//上传文件(图片)的原来的名字
String oldName = pictureFile.getOriginalFilename();
//截取文件(图片)的后缀,从.开始截取
String sux = oldName.substring(oldName.lastIndexOf("."));
//新建本地文件流
File file = new File("F:\\temp\\images\\" + newName + sux);
//写入本地磁盘
pictureFile.transferTo(file);//transferTo方法可以将上传的文件(图片)写入本地磁盘
//保存文件(图片)到数据库
item.setPic(newName + sux);
itemService.updateItem(item);
model.addAttribute("item", item);
model.addAttribute("msg", "修改商品信息成功");
return "itemEdit";
}
注意,考虑到实际情况,保存文件(图片)时一般不只你一个人保存,那么这个文件(图片)有的时候文件名可能会重复,你保存多了会把原来的文件(图片)给覆盖掉,这就不太合适了,所以需要使用UUID算法为每个文件(图片)生成一个新的文件名。
然后,来看一下updateItem方法中的形参,主要有Item、Model以及MultipartFile类型的pictureFile,我这里上传的图片是Item类中的一个属性,所以有了这个形参,那么就可以写到该类中。SpringMVC文件上传的类是MultipartFile,参数名称pictureFile必须和前台页面itemEdit.jsp中的name属性一致才行。上传图片成功之后的效果类似于下面这样。
并且图片的文件名也保存到了数据库表中,如下图所示。
最后总结一下,我上传文件的逻辑可能判断不严谨,如果说要更加严谨一点的话,上传文件的逻辑就应是这样的:首先判断有没有上传文件,如果上传了,那么对文件重新命名然后写到磁盘中。如果没有上传文件,那么我应该还是用原来的文件(图片)。读者如果有兴趣可以参考下面的代码。
@RequestMapping(value="/updateItem",method={RequestMethod.POST,RequestMethod.GET})
public String updateItem(Item item, MultipartFile pictureFile) throws Exception {
// 处理上传的单个图片
String originalFileName = pictureFile.getOriginalFilename();// 原始名称
// 上传图片
if (pictureFile != 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);//新图片
pictureFile.transferTo(newFile);// 将内存中的数据写入磁盘
item.setPic(newFileName);// 将新图片名称写到itemsCustom中
} else {
//如果用户没有选择图片就上传了,还用原来的图片
Item temp = itemsService.findItemsById(item.getId());
item.setPic(temp.getPic());
}
// 调用service更新商品信息,页面需要将商品信息传到此方法
itemService.updateItem(item);
return "forward:itemEdit.action";
}
到这里,单个文件的上传我就已总结完了。