Struts 的 Action 扮演 MVC(Model-View-Controller) 中的 Controller 角色
所有 Struts 的 request 都會先經過 Controller 在進行後端的 Model 計算並產生前端的 View
相信對於使用 Struts 的 programmer 來說,一般的 Action 都是不陌生的!!
但是,要如何可以讓使用者透過瀏覽器來下載檔案呢??
當然這檔案不是一個 static file,要對使用者的 request 產生一個 dynamic file
static file要做到就是直接將路徑指到 browser 的 URL 就OK
那 dynamic file呢?
Struts 提供一種 Action - DownloadAction,就如同一般的 Action 使用一樣
但是不同點在於需要 implement 一個 getStreamInfo method
所以我的 Controller 就會長成這樣:
public class MyController extends DownloadAction {
public StreamInfo getStreamInfo(...) {
}
}
基本的 class 架構就如上面的樣子,那 getStreamInfo method 呢?? 我要怎樣去完成??
我們先討論為何會有 getStreamInfo method 的存在
這個 method 是讓我們可以撰寫要讓使用者下載檔案的內容
觀察 Struts 的 DownloadAction source code 就可以發現,DownloadAction 是如何執行的:
1.首先呼叫我們撰寫的 getStreamInfo() method 來建立一個 StreamInfo object
2.接著就是由 StreamInfo object 中獲得 content type and inputStream
3.再來就是設定 browser 的 content type,告知 browser 要如何處理該檔案,如果是可以由外部存取的檔案,就會試著呼叫相關的 application 來支援
4.最後就是將 inputStream 的內容 copy 到 response 的 outputStream
使用者就可以透過 browser 取得檔案或是呼叫相關的 application 來處理檔案
知道流程後,我們就必須要撰寫 getStreamInfo() method 來符合流程囉~~
由上述的流程中,我們需要建立一個 StreamInfo object,裡面包含 content type and inputStream,所以重點就在於 StreamInfo object 的撰寫
觀察 Struts API 就會發現我們竟然找不到 StreamInfo interface 的內容!! 該不會 API 出錯??
身為一個 JAVA Programmer 的直覺,StreamInfo 一定是 DownloadAction 的 inner interface,所以我們要改查 DownloadAction.StreamInfo,果然不出我所料!!
既然是一個 interface 那我們就要接著觀察他有哪些 Known Implementing Classes
在 API 中立即就寫出:
All Known Implementing Classes:
DownloadAction.FileStreamInfo, DownloadAction.ResourceStreamInfo
所以我們就要觀察哪個 implementing class 是我們所需要的!!
從 DownloadAction.FileStreamInfo 中知道這是用來產生檔案下載的,而 DownloadAction.ResourceStreamInfo 是用來產生網路資源的
所以我們就鎖定 DownloadAction.FileStreamInfo class 來使用
決定好關鍵的 class 之後,就是開始撰寫我們 getStreamInfo() method 囉~~
然而,需要建立一個 DownloadAction.FileStreamInfo 物件就需要設定他的 content type and file object(因為 constructor 的 parameter)
所以我們就要先建立一個 file 物件來讓 DownloadAction 的 inputStream 可以完成第四項流程
我們需要將我們的 dynamic file 備份到 local 的 HD 中,再將我們存放的路徑給予 File object
所以我們的 getStreamInfo() method 會先有這樣的內容:
public StreamInfo getStreamInfo(...) {
String path = "/a.dat";
File file = new File(path);
}
再來就是 content type 的部分,content type 就是告知 browser 該如何去處理接著要下載的檔案,例如我們設定 application/pdf,browser 就會預設呼叫 Adobe Acrobat Reader 來讀取
但是這樣還是無法完成正確的下載,所以我們必須在 StreamInfo 物件送交之前,對 response 設定 Header 為 "Content-disposition",value 為 "attachment; filename=a.dat"
這段設定的目的在於,強制 browser 不要在 browser 本身開啟檔案,強制 browser 透過下載檔案的方式來處理,而 filename 的部分卻可以讓我們對於使用者下載的檔名給與預設值
所以最後完成的內容應該會包含這樣的樣子:
public class MyController extends DownloadAction {
public StreamInfo getStreamInfo(...) {
String path = "/a.dat";
File file = new File(path);
String contentType = "application/unknow";
//商業邏輯...
response.setHeader(
"Content-disposition",
"attachment; filename=a.dat");
return new FileStreamInfo(contentType,file);
}
}
這樣我們就可以完成讓使用者下載檔案的功能了!!
沒有留言:
張貼留言