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;
}