原文地址:http://www.blogjava.net/Unmi/archive/2009/06/17/282780.html
檔案下載給我們最直接的概念就是,給個檔案連結點擊就下載。似乎太簡單,然而 Struts2
卻把它作為一個獨立的學問來對待,理由有四:
1.
檔案名稱為中文時,直接點擊下載,連結可能會走樣(某些瀏覽器,URL 編碼的問題),致使無法下載。2.
不總是從下載實際的檔案,檔案內容有時候是動態產生的,如資料庫中的內容。
3.
對於知名的檔案類型不讓瀏覽器直接開啟,而是出現下載對話方塊儲存檔案。例如,要下載的檔案是 .txt 的,可能直接就在瀏覽器中顯示其內容。
4.
需要授權才能
下載
檔案
時
當然對於以上若干問題,Servlet/JSP 都能通過正確的 URL 編碼,回應標頭設定、許可權代碼控制解決,只是 Struts2
讓我們處理起來更方便了,內部原理自然是一樣的。
先來看下 Servlet 如何?檔案下載的,直接見代碼:
PrintWriter out = response.getWriter();<br />//不管實際類型,待下載檔案ContentType統一指定為 application/octet-stream<br />response.setContentType("application/octet-stream");<br />//中文檔案名稱必須轉碼為 ISO8859-1,否則為亂碼<br />String fileName = new String("文字檔.txt".getBytes(), "ISO8859-1");<br />//作為附件下載,相應的 "inline;filename = "+fileName 是線上(瀏覽器中顯示內容)開啟<br />response.setHeader("Content-Disposition", "attachment;filename=" + fileName);<br />//因為檔案編碼也為 ISO8859-1,所以內容須轉碼成 ISO8859-1,尚不知如何控制下載文字檔的編碼<br />//或有誰知道的,還請告訴我一下。 檔案內容可以從物理檔案中來,或者資料庫中讀取填入等等<br />out.write(new String("Servlet 檔案下載測試".getBytes(), "ISO8859-1"));<br />out.close();
知道了上面各行的含義,再來看下 Struts2
的解決方式,其實不過是把某些代碼的功能移入到了設定檔而已。在李剛所著的《Struts 2
權威指南》中說 Struts 實現檔案下載是由一個 download 攔截器。其實不然,只是一個
StreamResult(org.apache.struts2
.dispatcher.StreamResult)
而已,也不像實現檔案上傳那樣要額外的 JAR 包。在 StreamResult 中有以下幾個預設屬性要留意一下:
public static final String DEFAULT_PARAM = "inputName";
protected String contentType = "text/plain";
protected
String contentDisposition = "inline";
protected String inputName =
"inputStream";
protected InputStream inputStream;
protected int bufferSize = 1024;
StreamResult
的實現細節敬請閱讀它的原始碼,實現過程一言以蔽之就是:從 inputStream 擷取內容,以相應的
contentType、contentDisposition 和 bufferSize 輸出給瀏覽器,對 contentType 和
contentDisposition 的相應設定就能實現檔案下載,可對照前面 Servlet 的實現。看個實際的例子吧。
struts.xml
中 Action 的配置,假定 Action 類為 com.unmi.DownLoadAction
<action name="download" class="com.unmi.action.DownloadAction"><br /><result name="success" type="stream"><!--type 為 stream 應用 StreamResult 處理--><br /> <param name="contentType">application/octet-stream</param><!--預設為 text/plain--></p><p> <!-- 預設就是 inputStream,它將會指示 StreamResult 通過 inputName 屬性值的 getter 方法,<br /> 比如這裡就是 getInputStream() 來擷取下載檔案的內容,意味著你的 Action 要有這個方法 --><br /> <param name="inputName">inputStream</param></p><p> <!-- 預設為 inline(線上開啟),設定為 attachment 將會告訴瀏覽器下載該檔案,filename 指定下載文<br /> 件保有存時的檔案名稱,若未指定將會是以瀏覽的頁面名作為檔案名稱,如以 download.action 作為檔案名稱,<br /> 這裡使用的是動態檔案名稱,${fileName}, 它將通過 Action 的 getFileName() 獲得檔案名稱 --><br /> <param name="contentDisposition">attachment;filename="${fileName}"</param><br /> <param name="bufferSize">4096</param><!-- 輸出時緩衝區的大小 --><br /></result><br /></action>
說明:對於上面的配置其他參數可以用預設值,關鍵就是 contentDisposition 要設定為 attachment 才能提示下載,同時用
filename 指定檔案名稱,若直接指定非動態檔案名稱。
DownloadAction 代碼,需要實現
getInputStream() 返回輸入資料流;因前面用的動態檔案名稱,所以須加上 getFileName()
返迴文件名,若非動態檔案名稱,則該方法可省去。
package com.unmi.action;<br />import java.io.*;<br />import java.text.*;<br />import java.util.Date;<br />/**<br /> * 檔案下載的 Action<br /> * @author Unmi<br /> */<br />public class NetbookSerialAction {<br />public String execute() throws Exception {<br />//這裡可加入許可權控制<br />return "success";<br />}<br />//獲得下載檔案的內容,可以直接讀入一個物理檔案或從資料庫中擷取內容<br />public InputStream getInputStream() throws Exception {<br />//return new FileInputStream("somefile.rar"); 直接下載 somefile.rar<br />//和 Servlet 中不一樣,這裡我們不需對輸出的中文轉碼為 ISO8859-1<br />return new ByteArrayInputStream("Struts2 檔案下載測試".getBytes());<br />}<br />//對於配置中的 ${fileName}, 獲得下載儲存時的檔案名稱<br />public String getFileName() {<br />DateFormat df = new SimpleDateFormat("yyyy-MM-dd");<br />String fileName = "序號(" + df.format(new Date()) + ").txt";<br />try {<br />//中文檔案名稱也是需要轉碼為 ISO8859-1,否則亂碼<br />return new String(fileName.getBytes(), "ISO8859-1");<br />} catch (UnsupportedEncodingException e) {<br />return "impossible.txt";<br />}<br />}<br />}
謹記一個就是,要想下載的檔案名稱不亂碼就要以 ISO8859-1 字元集進行轉碼,內容會否亂碼可在調試中解決。
好啦,啟動服務,訪問
http://localhost:8080/teststruts2/download.action
,
瀏覽器便會提示下載 序號(2009-06-17).txt,內容為:“Struts2
檔案下載測試”。
參考:1.
淺
談Struts2
下載檔案
的方法
實現
2. struts2
多檔案動態下載及中文解決方案