关于结合Spring + Spring MVC 实现文件上传下载,之前总结了几篇
Spring MVC-09循序渐进之文件上传(基于Apache Commons FileUpload)
Spring MVC-09循序渐进之文件上传(基于Servlet3.0+内置功能)
Spring MVC-09循序渐进之文件上传(基于Servlet3.0+Html5客户端上传文件)
Spring MVC-10循序渐进之文件下载
这里我们选择使用基于Apache Commons FileUpload的方式
<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.2</version>
</dependency>
/o2o/src/main/resources/spring/spring-web.xml
<!-- 文件上传解析器 -->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="utf-8"></property>
<property name="maxUploadSize" value="10485760000"></property><!-- 最大上传文件大小 -->
<property name="maxInMemorySize" value="10960"></property>
</bean>
/o2o/src/main/webapp/WEB-INF/html/shop/shopoperation.html
type=“file” ,并且设置id,方便js中操作
<!-- 商铺图片 上传组件-->
<li class="item-content">
<div class="item-inner">
<div class="item-inner">
<div class="item-title label">商铺图片</div>
<div class="item-input">
<input type="file" id="shop-img">
</div>
</div>
</div>
</li>
html尾部引入shopoperation.js
<!-- 加载自定义的JS -->
<script type='text/javascript'
src='../resources/js/shop/shopoperation.js' charset='utf-8'></script>
/o2o/src/main/webapp/resources/js/shop/shopoperation.js
获取shopImg ,(因为只能上传一张图片,所以$(’#shop-img’)[0].files[0]) ,添加到formData中,使用ajax提交到后台
/**
* submit按钮触发的操作
*/
$('#submit').click(function() {
// 获取页面的值
省略....
省略....
// 图片
var shopImg = $('#shop-img')[0].files[0];
// 接收数据
var formData = new FormData();
// 和后端约定好,利用shopImg和 shopStr接收 shop图片信息和shop信息
formData.append('shopImg',shopImg);
// 转成JSON格式,后端收到后将JSON转为实体类
formData.append('shopStr',JSON.stringify(shop));
// 将数据封装到formData发送到后台
formData.append('verifyCodeActual',verifyCodeActual);
// 利用ajax提交
$.ajax({
url:registerShopUrl,
type:'POST',
data:formData,
contentType:false,
processData:false,
cache:false,
success:function(data){
if(data.success){
$.toast('提示信息:'+data.errMsg);
}else{
$.toast('提示信息:' + data.errMsg);
}
// 点击提交后 不管成功失败都更换验证码,防止重复提交
$('#kaptcha_img').click();
}
});
});
/o2o/src/main/java/com/artisan/o2o/web/shopadmin/ShopController#registerShop方法
控制层的方法接收到前端的请求后,从MultipartHttpServletRequest 中获取到CommonsMultipartFile类型的 shopImg,同时为了简化Controller层调用Service层的难度,这里我们对Service的addShop做了改造,这样就避免了将CommonsMultipartFile转换为File,而是通过CommonsMultipartFile的getInputStream()方法,以流的形式作为入参(因为Thumbnail也可以处理流)。
控制层调用Service层,Service层addShop方法根据入参获取到文件的后缀名后,写入shop的基本信息,然后调用工具类获取文件的存储路径,将图片打上水印存入对应的文件目录,最后更新到tb_shop。具体见 实战SSM_O2O商铺_10【商铺注册】Service层的实现
@RequestMapping(value = "/registshop", method = RequestMethod.POST)
@ResponseBody
public Map<String, Object> registerShop(HttpServletRequest request) {
Map<String, Object> modelMap = new HashMap<String, Object>();
// 0. 验证码校验
省略......
// 1. 接收并转换相应的参数,包括shop信息和图片信息
省略......
// 1.2 图片信息 基于Apache Commons FileUpload的文件上传
// Spring MVC中的 图片存在CommonsMultipartFile中
CommonsMultipartFile shopImg = null;
// 从request的本次会话中的上线文中获取图片的相关内容
CommonsMultipartResolver commonsMultipartResolver = new CommonsMultipartResolver(request.getSession().getServletContext());
if (commonsMultipartResolver.isMultipart(request)) {
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
// shopImg是和前端约定好的变量名
shopImg = (CommonsMultipartFile) multipartRequest.getFile("shopImg");
} else {
// 将错误信息返回给前台
modelMap.put("success", false);
modelMap.put("errMsg", "图片信息为空");
return modelMap;
}
// 2. 注册店铺
if (shop != null && shopImg != null) {
// Session TODO
// 店主persionInfo的信息,肯定要登录才能注册店铺。
// 所以这部分信息我们从session中获取,尽量不依赖前端,这里暂时时不具备条件,后续改造,先硬编码,方便单元测试
PersonInfo personInfo = new PersonInfo();
personInfo.setUserId(1L);
shop.setOwner(personInfo);
// 注册店铺
// se = shopService.addShop(shop, shopImg); 改造前的调用方式
// 这个时候,我们从前端获取到的shopImg是CommonsMultipartFile类型的,如何将CommonsMultipartFile转换为file.
// 网上也有将CommonsMultipartFile转换为File的方法,并通过maxInMemorySize的设置尽量不产生临时文件
// 这里我们换个思路,因为CommonsMultipartFile可以获取InputStream,Thumbnailator又可以直接处理输入流
// 因为InputStream中我们无法得到文件的名称,而thumbnail中需要根据文件名来获取扩展名,所以还要再加一个参数String类型的fileName
// 既然第二个和第三个参数都是通过shopImg获取的,为什么不直接传入一个shopImg呢?
// 主要是为了service层单元测测试的方便,因为service层很难实例化出一个CommonsMultipartFile类型的实例
ShopExecution se = null;;
try {
se = shopService.addShop(shop, shopImg.getInputStream(), shopImg.getOriginalFilename());
if (se.getState() == ShopStateEnum.CHECK.getState()) {
modelMap.put("success", true);
modelMap.put("errMsg", "注册成功");
} else {
modelMap.put("success", false);
modelMap.put("errMsg", se.getStateInfo());
}
} catch (IOException e) {
e.printStackTrace();
modelMap.put("success", false);
modelMap.put("errMsg", "addShop Error");
}
} else {
// 将错误信息返回给前台
modelMap.put("success", false);
modelMap.put("errMsg", "请输入店铺信息");
}
return modelMap;
}
处理成功后,返回前台JSON数据,给用户提示
代码地址: https://github.com/yangshangwei/o2o