你未看此花时,则此花与汝心同归于寂, 你来看此花时,此花颜色一时明白起来,便知此花不在你的心外。
上一章简单介绍了SpringMVC的数据验证和JSR 303国际化显示(十一),如果没有看过,请观看上一章。
一. SpringMVC的文件上传和下载
文件上传和下载功能是非常常用的,如上传用户的头像和上传文档等。 可以结合老蝴蝶以前写的文章Struts2的文件上传功能来结合性学习:Struts2实现单文件上传,多文件上传与下载(十)
SpringMVC在实现上传的时候,主要依赖于 org.springframework.web.multipart.MultipartFile 接口。
其接口定义为:
public abstract interface MultipartFile
extends InputStreamSource
{
public abstract String getName();
public abstract String getOriginalFilename();
public abstract String getContentType();
public abstract boolean isEmpty();
public abstract long getSize();
public abstract byte[] getBytes()
throws IOException;
public abstract InputStream getInputStream()
throws IOException;
public abstract void transferTo(File paramFile)
throws IOException, IllegalStateException;
}
其中,主要使用的是 getOriginalFilename()和 transferTo() 方法。 前者是获取 上传文件的名称,后者是进行文件的复制。
MultipartFile 接口的实现类是: org.springframework.web.multipart.commons.CommonsMultipartFile
这个类也与Struts2一样,同样需要依赖于apache 提供的文件上传jar包。
commons-fileupload-1.3.1.jar,commons-io-2.4.jar
SpringMVC在上传文件的时候,可以将其放置在方法里面,单独使用,也可以将其与相应的对象类进行关联使用。 通常选用第二种方式。
也同样是简单的User 类。 比以前多添加了一个 image 属性,来进行上传。
所拥有的属性有: (为节省篇福,避免出现像前几章那样长的坏文章形式,老蝴蝶就不贴完整的代码了。抱歉。)
private Integer id;
private String name;
private String sex;
private Integer age;
private String descrption;
//有一个上传图片的路径的属性
private String image;
均需要导入 上面那两个jar包。 不要忘记噢
二. SpringMVC实现单文件上传,方法内参数形式
二.一 前端界面1 login.jsp ,上传文件
要注意, 提交的方法类型为post, enctype类型为 multipart/form-data
用的是普通的 type=file 框,并没有form:file 标签。
<body>
<h2>两个蝴蝶飞,文件上传使用</h2>
<form:form commandName="user" action="login.action"
enctype="multipart/form-data" type="post">
<form:label path="name">姓名:</form:label>
<form:input path="name"/> <br/><br/>
<input type="file" name="file"> <br/><br/>
<form:button>提交</form:button>
</form:form>
</body>
二.二 前端界面2 list.jsp,展示文件
<body>
你好,${message} <img src="${pageContext.request.contextPath}${user.image}" width=100 height=100>
</body>
二.三 后端处理, UserAction
//转到登录的页面
@RequestMapping(value="toLogin")
public String toLogin(Model model){
model.addAttribute("user",new User());
return "user/login";
}
//绑定到user对象。 value="file" 要与form表单的file框的name相同.
@RequestMapping(value="login")
public String login(User user,Model model,HttpServletRequest request,
@RequestParam(value="file") MultipartFile file){
System.out.println("输出名称:"+user.getName());
System.out.println("输出MultipartFile接口中的各个方法的值");
System.out.println("contentType:"+file.getContentType());
System.out.println("name:"+file.getName());
System.out.println("originalFilename:"+file.getOriginalFilename());
System.out.println("size:"+file.getSize());
String message="";
//进行上传文件
if(!file.isEmpty()){ //不为空,即上传了文件
//1. 获取服务器的文件
String path=request.getServletContext().getRealPath("/upload");
//2. 获取上传的文件的名称
String fileName=file.getOriginalFilename();
//3. 实例化构建文件。
File filePath=new File(path,fileName);
//4.判断文件,如果父不存在的话,即upload文件夹不存在的话,就创建父级文件夹。
if(!filePath.getParentFile().exists()){
filePath.getParentFile().mkdirs();
}
try {
file.transferTo(new File(path+File.separator+fileName));
message="文件上传成功";
//设置上传的路径,这里手动设置
user.setImage("/upload"+"/"+fileName);
System.out.println("输出图片路径:"+user.getImage());
} catch (IllegalStateException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
message="文件上传失败";
} catch (IOException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
message="文件上传失败";
}
}else{
message="没有上传文件";
}
model.addAttribute("user",user);
model.addAttribute("message",message);
return "user/list";
}
二.四 springmvc.xml 配置文件中配置文件上传的 CommonsMultipartResolver
在 <mvc:annotation-driven></mvc:annotation-driven> 里面,并没有默认提供关于文件上传的解析器。
需要单独添加
<mvc:annotation-driven></mvc:annotation-driven>
<!-- 添加文件上传所用的bean -->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 上传文件大小上限,单位为字节(10MB) -->
<property name="maxUploadSize">
<value>10000000</value>
</property>
<!-- 请求的编码格式,必须和jSP的pageEncoding属性一致,以便正确读取表单的内容,默认为ISO-8859-1 -->
<property name="defaultEncoding">
<value>UTF-8</value>
</property>
</bean>
二.五 重启服务器,进行验证
要上传的图片是:
进行上传:
上传成功,页面跳转展示:
控制台打印输出:
Tomcat 服务器上,upload 文件夹下有这个图片文件。
文件上传成功。 相比较Struts2来说,非常非常简单。
三. SpringMVC实现单文件上传,与对象关联形式
并不是将 MultipartFile 放置在方法里面了,而是放置在User 对象里面。
三.一 User.java 中添加一个MultipartFile类型 的属性。
//定义属性为imageFile
private MultipartFile imageFile;
要实现setter 和getter的方法。
三.二 前端login.jsp 需要改变一下,将type=file的name改成 imageFile .
<form:form commandName="user" action="login.action"
enctype="multipart/form-data" type="post">
<form:label path="name">姓名:</form:label>
<form:input path="name"/> <br/><br/>
<!--与user 类中的那个属性值对应-->
<input type="file" name="imageFile"> <br/><br/>
<form:button>提交</form:button>
</form:form>
list.jsp 保持不变。
三.三 后端 UserAction 需要改变一下,去除多余的输出
//转到登录的页面
@RequestMapping(value="toLogin")
public String toLogin(Model model){
model.addAttribute("user",new User());
return "user/login";
}
//绑定到user对象。
@RequestMapping(value="login")
public String login(User user,Model model,HttpServletRequest request){
String message="";
MultipartFile file=user.getImageFile();
/*后面这些都一样了*/
//进行上传文件
if(null!=file&&!file.isEmpty()){ //不为空,即上传了文件
//1. 获取服务器的文件
String path=request.getServletContext().getRealPath("/upload");
//2. 获取上传的文件的名称
String fileName=file.getOriginalFilename();
//3. 实例化构建文件。
File filePath=new File(path,fileName);
//4.判断文件,如果父不存在的话,即upload文件夹不存在的话,就创建父级文件夹。
if(!filePath.getParentFile().exists()){
filePath.getParentFile().mkdirs();
}
try {
file.transferTo(new File(path+File.separator+fileName));
//设置图片的路径。
user.setImage("/"+"upload"+"/"+fileName);
message="文件上传成功";
} catch (IllegalStateException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
message="文件上传失败";
} catch (IOException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
message="文件上传失败";
}
}else{
message="没有上传文件";
}
model.addAttribute("user",user);
model.addAttribute("message",message);
return "user/list";
}
三.四 springmvc.xml 的配置与上面的一样,不需要改变
三.五 运行服务器,进行测试
用这张图片进行测试
点击提交,运行成功,跳转到list.jsp页面。页面展示:
哈哈,这猫,很形象啊。
四. 多文件上传及多文件列表展示, 与对象关联的形式
springmvc 也同样支持多文件上传, 需要把 MultipartFile 单对象改成 数组形式 MultipartFile [] 或者是集合形式 List ,习惯用数组形式来接收。
当然,也用两种用法,一个是方法参数,另外一个就是与对象关联, 这里用对象关联的形式进行讲解,顺便把 文件的展示列表也给讲了。
四.一 重新定义 User.java 类的属性
将image 变成 images, 并设置成集合的形式。 imageFile 变成imageFiles, 并且成为数组的形式。
//有多个上传图片的路径的属性
private List<String> images ;
//定义属性为imageFiles
private MultipartFile [] imageFiles;
老蝴蝶在这儿省略了 setter和getter方法。
四.二 上传文件的 login.jsp 页面
将单文件变成多文件
<body>
<h2>两个蝴蝶飞,文件上传使用</h2>
<form:form commandName="user" action="login.action"
enctype="multipart/form-data" type="post">
<form:label path="name">姓名:</form:label>
<form:input path="name"/> <br/><br/>
<!-- 可以使用js来动态设置,这里就指定为3个 -->
<input type="file" name="imageFiles"> <br/><br/>
<input type="file" name="imageFiles"> <br/><br/>
<input type="file" name="imageFiles"> <br/><br/>
<form:button>提交</form:button>
</form:form>
</body>
四.三 展示文件的 list.jsp页面
要展示多个, 用c:forEach 标签
不要忘记添加 jstl的core标签
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
页面展示:
<body>
你好,${message}<br/>
<c:forEach items="${user.images}" var="imgPath">
<img src="${pageContext.request.contextPath}${imgPath}" width=100 height=100> <br/>
</c:forEach>
</body>
四.四 后端UserAction 的处理
//转到登录的页面
@RequestMapping(value="toLogin")
public String toLogin(Model model){
model.addAttribute("user",new User());
return "user/login";
}
//绑定到user对象。
@RequestMapping(value="login")
public String login(User user,Model model,HttpServletRequest request){
String message="";
MultipartFile[] files=user.getImageFiles();
if(files!=null&&files.length>0){
List<String> images=new ArrayList<String>();
for(MultipartFile file:files){
//进行单个文件的判断。
if(file!=null&&!file.isEmpty()){ //不为空,即上传了文件
//1. 获取服务器的文件
String path=request.getServletContext().getRealPath("/upload");
//2. 获取上传的文件的名称
String fileName=file.getOriginalFilename();
//3. 实例化构建文件。
File filePath=new File(path,fileName);
//4.判断文件,如果父不存在的话,即upload文件夹不存在的话,就创建父级文件夹。
if(!filePath.getParentFile().exists()){
filePath.getParentFile().mkdirs();
}
try {
file.transferTo(new File(path+File.separator+fileName));
//设置图片的路径。
String imageName="/upload"+"/"+fileName;
images.add(imageName);
message="文件上传成功";
} catch (IllegalStateException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
message="文件上传失败";
} catch (IOException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
message="文件上传失败";
}
}
//for 遍历之后,再设置
user.setImages(images);
}
}else{
message="没有上传文件";
}
model.addAttribute("user",user);
model.addAttribute("message",message);
return "user/list";
}
四.五 springmvc.xml 配置文件,不需要 改变
四.六 运行服务器,进行测试
上传提交:
提交后,展示图片
Tomcat服务器 路径显示:
多文件上传成功。
五. 下载文件
众所皆知, 链接到正确的位置,可以进行下载。 下面,用a 链接看看。
五.一 a 链接显示
将多文件列表那个 list.jsp 的显示图片 <img> ,改成 <a> 链接的话,看看效果怎么样。
<body>
你好,${message}<br/>
<ul style="list-style: none;">
<c:forEach items="${user.images}" var="imgPath">
<%-- <img src="${pageContext.request.contextPath}${imgPath}" width=100 height=100> <br/> --%>
<li>
<a href="${pageContext.request.contextPath}${imgPath}">${imgPath}</a>
</li>
</c:forEach>
</ul>
</body>
刷新后展示为:
点击链接,发现会进行查看,并不会下载。
用JS 强行下载呢?
五.二 JS 强制 a链接下载
将上面 的 list.jsp 继续优化,改成,用js事件来进行下载。
<body>
你好,${message}<br/>
<ul style="list-style: none;">
<c:forEach items="${user.images}" var="imgPath">
<%-- <img src="${pageContext.request.contextPath}${imgPath}" width=100 height=100> <br/> --%>
<li>
<a href="javascript:void(0);" onclick="openDownloadDialog('${pageContext.request.contextPath}${imgPath}')">${imgPath}</a>
</li>
</c:forEach>
</ul>
<script>
function openDownloadDialog(url){
var first = "" , last = "" , name = "" , houZhui = "";
first = url.lastIndexOf("/");//最后一个/的位置
last = url.lastIndexOf(".");//最后一个.的位置
name = url.substr(first+1,last-first-1);//照片名称
houZhui = url.substr(url.lastIndexOf("."));//后缀
var aLink = document.createElement('a');//创建一个虚拟的a标签
aLink.href = url;//这只a标签的url
aLink.download = name + houZhui; // 指定保存文件名,可以不要后缀
var event;
event = document.createEvent('MouseEvents');//创建一个新的MouseEvent对象
event.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
aLink.dispatchEvent(event);
}
</script>
</body>
刷新页面,当点击链接之后,会弹出下载对话框,可以进行下载。
当这上面,只是js的前端下载, 还需要学习后端的下载。
五.三 服务器后端下载
在UserAction 中添加一个download() 下载的方法,传入的是文件的路径 (也可以传入文件的名称),进行操作
@RequestMapping(value="download")
public ResponseEntity<byte[]> download(HttpServletRequest request,
@RequestParam(value="filePath") String fileName) throws UnsupportedEncodingException,
IOException{
//1 获取项目路径
String contextPath=request.getServletContext().getRealPath("/");
System.out.println("输出name:"+fileName);
// 2 拼接下载的文件的路径
File file=new File(contextPath+fileName);
System.out.println("输出文件路径:"+file.toString());
//3 将下载的名称,进行格式转换
String downFileName=new String(fileName.getBytes("utf-8"),"ISO-8859-1");
//4 设置浏览器头部信息,可以下载的设置,设置中文编码,避免出现中文不能下载的情况。
HttpHeaders headers=new HttpHeaders();
headers.setContentDispositionFormData("attachment", downFileName);
//常见的格式,application/oct_stream
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
return new ResponseEntity<byte[]>(FileUtils.readFileToByteArray(file),
headers,HttpStatus.CREATED);
}
在前端 重写 list.jsp, a链接的href提交到后端。
<ul style="list-style: none;">
<c:forEach items="${user.images}" var="imgPath">
<li>
<a href="download.action?filePath=${imgPath}">${imgPath}</a>
</li>
</c:forEach>
</ul>
重启服务器,进行验证。
谢谢!!!