1. 程式人生 > >Java Web基礎知識之檔案下載:當你下載檔案的時候到底發生了什麼?

Java Web基礎知識之檔案下載:當你下載檔案的時候到底發生了什麼?

從網上下載檔案幾乎是每個人都會遇到的,不管是圖片、文字檔案還是一些視訊,但是我們真的知道在下載的過程中發生了什麼嗎?本文章就學習一下其中的原理。

關於檔案下載存在靜態下載和動態下載兩種,靜態下載是比較容易的,我們平常在網上對很多圖片和和視訊等的下載有很多其實就是靜態下載,那麼到底什麼是靜態下載?靜態下載可以理解為對靜態資源的下載,靜態資源是已經存在於web應用程式的目錄或者其子目錄中的檔案等,不需要通過程式控制進行改變的,可以直接通過瀏覽器直接進行訪問既可以直接下載的,這種可以稱之為靜態下載;但是並不是左右的資源都是靜態資源,有的檔案或者圖片可能並不儲存在應用程式目錄下,而可能是在WEB-INF目錄中,抑或是在伺服器的其他目錄中,還可以是在資料庫中,另外也可以通過程式控制生成檔案,當出現這種情況時就不是靜態下載能夠解決的了,就要通過程式設計的手段來發送資源。

一、 靜態下載

使用HTML標籤<a>來實現靜態下載,如下直接請求應用程式目錄下的檔案:
<body>
	<a href="abc.txt">靜態檔案下載</a>
</body>
注意如果直接點選的話並不會下載檔案而是在瀏覽器中顯示檔案的內容,看它的響應頭如下: 可以看出content-type是text/plain,這是這個首部決定瀏覽怎樣呈現伺服器返回的資料,但是可以通過另存為來完成檔案的下載。因為是一個靜態檔案,我們沒辦法程式設計改變它的返回的content-type的值,這些都是servlet容器來決定的,類似如果是png圖片則返回的型別是image/png。

二、 動態下載

動態下載就可以通過程式來控制下載的過程以及新增相應的資訊。我們首先設定一下JSP檔案的下載,因為我們可以通過設定它的content-type來告訴瀏覽器怎麼處理這個資料。

1、 JSP頁面下載

要下載的頁面如下:
<%@page import="java.util.Date"%>
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<!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=ISO-8859-1">
<title>JSP File</title>
</head>
<body>
	<%
		response.setContentType("application/x-msdownload");
	%>
	
	Now is <%=new Date(System.currentTimeMillis()) %>
</body>
</html>
還是使用如下HTML標籤:
<a href="content.jsp">JSP下載</a>
此時在點選該連結就會彈出save as對話方塊(在chrome下面測試),但是如果你使用微軟的Microsoft Edge瀏覽器的話則會直接顯示該頁面內容(Edge另存為也會下載檔案)。這裡使用的content-type首部的內容是"application/x-msdownload",這是告訴瀏覽器這是一個要儲存到本地的下載的檔案,雖然我們告訴了,但是具體瀏覽器是怎麼處理的我們是不知道的,所以對這個首部內容的處理不同的瀏覽器有不同的處理方式,所以這種辦法並不太可靠。 鑑於以上的情況,我們可以使用一個Http首部來完成下載工作,這就是content-disposition,使用這個首部是告訴瀏覽器不要參與處理,而是要客戶自己來處理該檔案,在chrome中實驗的結果就是也會彈出save as對話方塊,與使用content-type:application/x-msdownload有相同的效果;但是悲催的是使用微軟家的Edge雖然可以下載檔案,但是不會像chrome一樣彈出對話方塊,而是直接下載(微軟的瀏覽器更新了為什麼還是這麼特立獨行。。。),如下使用:
<body>
	<%
		response.addHeader("content-disposition", "attachment;filename=hello.jsp");
	%>
	
	Now is <%=new Date(System.currentTimeMillis()) %>
</body>
關於這個首部在使用的時候一定要加上attachment,並且還可以加上返回的檔名,注意這個filename屬性和真實的檔名可以不一樣,但是返回的檔名是以這個屬性的值為準的。還有要提一下,不知道網上有的朋友提到如果使用content-disposition這個首部,必須設定content-type值為"application/x-msdownload",但是根據我的試驗並不需要,如果有問題我們可以一起探討!!
還有第三種辦法,那就是使用content-type屬性的"application/octet-stream",這個屬性值表示是任意的位元組流,會強制瀏覽器開啟save as對話方塊來儲存檔案,這個在你對mime型別並不瞭解時可以使用,這樣就不用擔心每種檔案型別的mime型別是什麼了。在使用這個屬性值的時候,效果和使用content-disposition是一樣的,使用如下:
<body>
	<%
		response.setContentType("application/octet-stream");
	%>
	
	Now is <%=new Date(System.currentTimeMillis()) %>
</body>
下面總結一下這三種方式的效果:
Chrome Microsoft Edge
只使用content-type:application/x-msdownload 開啟save as對話方塊 直接在瀏覽器顯示檔案資訊
只使用content-disposition 開啟save as對話方塊 下載檔案,但不會開啟save as對話方塊
只使用content-type:application/octet-stream 開啟save as對話方塊 直接在瀏覽器顯示檔案資訊
在此註明:上述的實驗的檔案都是jsp檔案。

2、 程式設計下載

在這裡我們實現一個圖片的下載來具體的說明,程式碼如下:
public class DownloadServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {		
		response.setContentType("application/x-msdownload");
		
		// get file path in the server file system
//		String directory = request.getServletContext().getRealPath("/");
//		System.out.println(directory);
//		BufferedInputStream input = new BufferedInputStream(new FileInputStream(new File(directory, "abc.png")));
		
		InputStream is = request.getServletContext().getResourceAsStream("/abc.png");
		BufferedInputStream input = new BufferedInputStream(is);
		
		OutputStream os = response.getOutputStream();
		byte[] bytes = new byte[input.available()];
		input.read(bytes);
		os.write(bytes);
		input.close();
	}
}
這個servlet的URl是"/downServlet",此處有一個問題要注意,就是在伺服器上檔案的路徑問題,此處開始時使用的是getReal()方法來獲取檔案在伺服器上的絕對路徑,這不是一個好方法,因為這個API有一個問題,見如下連結。所以推薦使用的方法還是使用ServletContext的getResource()來獲得相對於web應用程式根目錄的資原始檔
  • 當只使用content-type:application/x-msdownload時:chrome會開啟save as對話方塊,但是檔名是downServlet而且沒有後綴png,即沒有識別出文件型別,使用Edge時則是直接顯示該圖片;
  • 當只使用content-disposition時,如下:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {		
	response.addHeader("content-disposition", "attachment; filename=abc.png"); 
	
	InputStream is = request.getServletContext().getResourceAsStream("/abc.png");
	BufferedInputStream input = new BufferedInputStream(is);
	
	OutputStream os = response.getOutputStream();
	byte[] bytes = new byte[input.available()];
	input.read(bytes);
	os.write(bytes);
	input.close();
}
chrome會開啟save as對話方塊,並且檔名就是content-disposition中設定的,並且還識別除了該檔案是png型別的圖片,在Edge中則會直接下載,沒有對話方塊,就是這麼任性。
  • 當只使用content-type:application/octet-stream時,效果和只使用application/x-msdownload一樣;
綜上所述我們給出比較好的使用辦法,必須使用content-disposition來說明檔名和型別;另外最好使用content-type:octet-stream來說明content-type,這樣也避免了我們對mime型別陌生的問題,但是如果你熟悉mime型別的話,最好還是指明該型別為好。 相關文章: