SpringMVC 实现文件下载文件
利用程序实现下载需要设置两个报头:
- 1.Web服务器需要告诉浏览器其所输出内容的类型不是普通文本文件或HTML文件,而是一个要保存到本地的下载文件。设置Content-Type 的值为:application/x-msdownload。
- 2.Web服务器希望浏览器不直接处理相应的实体内容,而是由用户选择将相应的实体内容保存到一个文件中,这需要设置Content-Disposition报头。该报头指定了接收程序处理数据内容的方式,在HTTP应用中只有attachment是标准方式,attachment表示要求用户干预。在attachment后面还可以指定filename参数,该参数是服务器建议浏览器将实体内容保存到文件中的文件名称。
浏览文件
提供一个页面浏览文件目录,如果不清楚应该在哪存放文件,可以先输出realpath,再进入目录新建文件夹以及存放部分文件。
@RequestMapping("file/show")
public String show(HttpServletRequest request, Model model) {
System.out.println("请求文件查看");
String realpath = request.getServletContext().getRealPath("") + "fileUpload\\temp\\";
File dir = new File(realpath);
File files[] = dir.listFiles();
// 获取该目录下的所有文件名
ArrayList<String> fileName = new ArrayList<String>();
for (int i = 0; i < files.length; i++) {
fileName.add(files[i].getName());
}
model.addAttribute("files", fileName);
return "showDownFiles";
}
浏览文件jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort()
+ path + "/";
%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<c:forEach var="filename" items="${files}">
<a href="${pageContext.request.contextPath }/file/down2?filename=${filename}">${filename}</a>
<br />
</c:forEach>
<br/>
</body>
</html>
结果如下:
使用ResponseEntity
ResponseEntity是一个响应实体,其中包含了返回的头部信息,状态信息以及内容。如下:
因此可以将文件解析成二进制流存储进ResponseEntity的body中,同时设置其响应头与状态便可以进行下载。
@RequestMapping("/file/down2")
public ResponseEntity<byte[]> download(@RequestParam String filename, HttpServletRequest request)
throws IOException {
//通过存放文件目录和文件名获取文件全路径
String filePath = request.getServletContext().getRealPath("") + "fileUpload\\temp\\" + filename;
File file = new File(filePath);
//定义响应头并设置下载文件报头
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type", "application/x-msdownload");
headers.add("Content-Disposition", "attachment; filename=" + URLEncoder.encode(filename, "UTF-8"));
//返回一个响应实体(body,header,status)
return new ResponseEntity<byte[]>(FileUtils.readFileToByteArray(file), headers, HttpStatus.OK);
}
这里需要注意的是,使用header的setContentType里面,使用的是MediaType类的常量,其中好像并没有找到"application/x-msdownload"类型,经过网络查询发现使用的是:MediaType.APPLICATION_OCTET_STREAM,不过我测试的时候并没有效果(后来发现是status的问题,使用HttpStatus.OK就可以了),因此采用了add手动添加。
还有一点就是,使用ResponseEntity,需要一次性将读取出来,如果文件过大可以就会发生OOM异常。如果只有小文件的传输,可以使用ResponseEntity;一旦文件过大,建议采用下面的方式。不过ResponseEntity的好处就在于简洁,只需要获取文件,设置头,然后封装到ResponseEntity返回。
使用Java常规方法
Java常规方式,有点类似json响应,服务器接收用户下载文件请求时,获取响应的输出流,加载服务器的文件作为输入流,然后一边从本地输入,一边向客户端传输。这种方式就有效的解决了文件过大会产生OOM异常的问题。同时,如果客户端的连接异常关闭则会抛出异常,可以进行有效的进行捕获进行处理。也可以扩展下载量统计,断点下载(RandomAccessFile)等功能。
@RequestMapping("file/down2")
public String down(@RequestParam String filename, HttpServletRequest request, HttpServletResponse response) {
System.out.println("请求文件下载");
FileInputStream in = null; // 输入流
ServletOutputStream out = null; // 输出流
try {
// 获得文件路径
String aFilePath = request.getServletContext().getRealPath("") + "fileUpload\\temp\\";
// 设置下载文件使用的报头
response.setHeader("Content-Type", "application/x-msdownload");
response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(filename, "utf-8"));
// 读入文件
in = new FileInputStream(aFilePath + "\\" + filename);
// 得到响应对象的输出流,用于向客户端输出二进制数据
out = response.getOutputStream();
out.flush();
int len = 0;
byte b[] = new byte[1024];
while ((len = in.read(b)) != -1 & in != null) {
out.write(b, 0, len);
}
out.flush();
} catch (Exception e) {
e.printStackTrace();
try {
in.close();
} catch (IOException e1) {
}
try {
out.close();
} catch (IOException e1) {
}
}
return null;
}