1. 文件上传
1.1 简介
SpringMVC对文件上传提供了支持,基于commonsfileupload
1.2 用法
步骤:
1. 添加jar包
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
2. 编写jsp页面
在view文件加下新建一个file.jsp文件,内容如下
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>文件上传</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/file/uploads" method="post" enctype="multipart/form-data">
用户名:<input type="text" name="username"/><br>
文件1:<input type="file" name="files"/><br>
文件2:<input type="file" name="files"/><br>
文件3:<input type="file" name="files"/><br>
<input type="submit" value="上传"/>
</form>
</body>
</html>
请求方式为POST,且enctype
的值必须为multipart/form-data
3. 配置文件解析器
在核心配置文件springmvc.xml中添加如下代码
<! 配置文件解析器,id名称必须为multipartResolver >
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSizePerFile" value="5000000"/>
</bean>
注意:文件解析器的id名称必须为multipartResolver
文件解析器有多个属性,可以对其进行更详细的配置
4. 通过CommonsMultipartFile来接收文件
我们打算把文件上传到WEB-INF文件夹下的upload文件夹里,并且给每个上传的文件重新命名。
首先新建一个工具类UploadUtils,添加为文件重命名的方法getUUIDName()
public class UploadUtils {
//为上传的文件命名一个唯一的名字
public static String getUUIDName(String filename){
int index = filename.lastIndexOf(".");
//文件后缀
String suffix = filename.substring(index);
String uuid = UUID.randomUUID().toString();
return uuid + suffix;
}
}
然后新建FileController类,添加上传方法。
@Controller
@RequestMapping("/file")
public class FileController {
@RequestMapping("/uploads")
public String upload(String username, @RequestParam CommonsMultipartFile[] files, HttpSession session, HttpServletRequest req) {
System.out.println("用户名:" + username);
//获取上传目录的物理路径
String path = session.getServletContext().getRealPath("/WEB-INF/upload/");
System.out.println("path:" + path);
for(CommonsMultipartFile file: files){
String fileName = file.getOriginalFilename();
System.out.print("文件原名:" + fileName);
String uuidName = UploadUtils.getUUIDName(fileName);
System.out.println(",重命名后的名字:" + uuidName);
File filepath = new File(path, fileName);
if(!filepath.getParentFile().exists()){
filepath.getParentFile().mkdirs();
}
try {
file.transferTo(new File(path, uuidName));
} catch (IOException e) {
e.printStackTrace();
}
}
return "success";
}
}
注意:文件是上传到tomcat服务器中的upload文件夹中了,并不会上传到本地磁盘中。
2. 文件下载
文件下载有两种方式:
- 使用OutputStream
- 使用ResponseEntity
2.1 使用OutputStream
传递文件名的两种方式:
- 使用请求参数传递文件名
- 使用rest风格传递文件名
步骤:
1. 预先往服务器里上传几个文件
假设我们要从tomcat服务器上WEB-INF/upload/下载文件,首先要确保其文件夹下有文件。
于是需要往服务器目录里上传几个文件。刚好可以利用上节所学的文件上传的知识。
为了方便下载,上传之后将文件名改为1.png、2.png等。
2. 添加href下载链接
我们先使用第一种传递文件名的方式:使用请求参数传递文件名,如下:
<a href="${pageContext.request.contextPath}/file/download?filename=1.png">下载文件</a>
3. 通过OutputStream接收文件
@RequestMapping("/download")
public void download(String filename, HttpSession session, HttpServletResponse resp){
//首先获取文件所在的路径
String path = session.getServletContext().getRealPath("/WEB-INF/upload/");
File file = new File(path, filename);
try {
//将输入流转换为输出流
StreamUtils.copy(new FileInputStream(file), resp.getOutputStream());
} catch (IOException e) {
e.printStackTrace();
}
}
4. 细节
- 如果在下载的时候,不设置下载方式,会默认以浏览器打开,而不是下载到本地。如果是以附件的方式,则可以下载。
如果在下载的时候不规定文件的名字,下载下来的文件默认的名字为download
,我们需要在将输入流转换为输出流之前加上如下代码resp.setHeader("content-disposition", "attachment;filename=" + filename);
- 如果文件名中有中文字符,显示会出现异常,所以编码格式也需要做一下规定。如下所示
filename = new String(filename.getBytes("utf-8"), "iso8859-1");
因此,完善之后的下载相关代码如下所示
@RequestMapping("/download")
public void download(String filename, HttpSession session, HttpServletResponse resp){
String path = session.getServletContext().getRealPath("/WEB-INF/upload/");
File file = new File(path, filename);
try {
filename = "movie-山楂树之恋.png";
filename = new String(filename.getBytes("utf-8"), "iso8859-1");
resp.setHeader("content-disposition", "attachment;filename=" + filename);
StreamUtils.copy(new FileInputStream(file), resp.getOutputStream());
} catch (IOException e) {
e.printStackTrace();
}
}
5. 使用rest风格传递文件名
如果使用rest风格传递文件名,则在url中以占位符的形式将文件名写入,在Controller中使用注解@PathVariable
获取值,如下例
<a href="${pageContext.request.contextPath}/file/download2/1.png">下载文件</a>
@RequestMapping("/download2/{filename}")
public void download2(@PathVariable String filename, HttpSession session, HttpServletResponse resp){
String path = session.getServletContext().getRealPath("/WEB-INF/upload/");
File file = new File(path, filename);
try {
filename = "movie-山楂树之恋.png";
filename = new String(filename.getBytes("utf-8"), "iso8859-1");
resp.setHeader("content-disposition", "attachment;filename=" + filename);
StreamUtils.copy(new FileInputStream(file), resp.getOutputStream());
} catch (IOException e) {
e.printStackTrace();
}
}
但是这种方式有一个问题,就是以rest风格传递文件名,如果参数放在url的最后且参数有后缀,如.jpg
,就会把后缀删除,从而导致找不到需要下载的文件。
解决这个问题的方法有两个:
1.在核心配置文件springmvc.xml中配置HandlerMapping的属性,使自动截取后缀的属性值为false。但是由于现在配置HandlerMapping的属性都是自动装配,一般不改动这部分的代码,所以这种方法很少用。
<!-- 2.配置HandlerMapping-->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
<property name="useSuffixPatternMatch" value="false"/>
</bean>
- 将文件名夹在url中间
<a href="${pageContext.request.contextPath}/file/1.png/download2">下载文件</a>
@RequestMapping("/{filename}/download2")
public void download2(@PathVariable String filename, HttpSession session, HttpServletResponse resp){
...
2.2 使用ResponseEntity
有了以上的基础,使用ResponseEntity下载数据就相当简单了,使用ResponseEntity下载数据需要返回ResponseEntity值,这个值包含三个参数:文件数据、响应头、状态码,只需要创建这三个参数,用其构造出一个ResponseEntity对象就可以了
<a href="${pageContext.request.contextPath}/file/download3/filename=1.png">下载文件:使用ResponseEntity</a>
@RequestMapping("/download3")
public ResponseEntity<byte[]> download2(String filename, HttpSession session) throws IOException {
String path = session.getServletContext().getRealPath("/WEBINF/upload/");
File file=new File(path,filename);
//ResponseEntity<byte[]>需要三个参数:文件数据、响应头、状态码
//文件数据
byte[] bytes = FileUtils.readFileToByteArray(file);
//响应头
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add("contentdisposition","attachment;filename="+filename);
return new ResponseEntity<byte[]>(bytes,httpHeaders, HttpStatus.OK);
}