了解ASP.NET MVC幾種ActionResult的本質:FileResult
FileResult是一個基於文件的ActionResult,利用FileResult我們可以很容易地將從某個物理文件的內容響應給客戶端。ASP.NET MVC定義了三個具體的FileResult,分別是FileContentResult、FilePathResult和FileStreamResult。在這篇文章中我們將探討三種具體的FileResult是如何將文件內容對請求進行響應的。[本文已經同步到《How ASP.NET MVC Works?》中]
目錄
一、FileResult
二、FileContentResult
三、FilePathResult
四、FileStreamResult
五、實例演示:通過FileResult發布圖片
如下麵的代碼片斷所示,FileResult具有一個表示媒體類型的隻讀屬性ContentType,該屬性在構造函數中被初始化。當我們基於某個物理文件創建相應的FileResult對象的時候應該根據文件的類型指定媒體類型,比如說目標文件是一個.jpg圖片,那麼對應的媒體類型為“image/jpeg”,對於一個.pdf文件,則采用“application/pdf”。
1: public abstract class FileResult : ActionResult
2: {
3: protected FileResult(string contentType);
4: public override void ExecuteResult(ControllerContext context);
5: protected abstract void WriteFile(HttpResponseBase response);
6:
7: public string ContentType { get; }
8: public string FileDownloadName { get; set; }
9: }
針對文件的響應具有兩種形式,即內聯(Inline)和附件(Attachment)。一般來說,前者會利用瀏覽器直接打開響應的文件,而後者會以獨立的文件下載到客戶端。對於後者,我們一般會為下載的文件指定一個文件名,這個文件名可以通過FileResult的FileDownloadName屬性來指定。文件響應在默認情況下采用內聯的方式,如果需要采用附件的形式,需要為響應創建一個名稱為Content-Disposition的報頭,該報頭值的格式為“attachment; filename={ FileDownloadName }”。
FileResult僅僅是一個抽象類,文件內容的輸出實現在抽象方法WriteFile中,該方法會在重寫的ExecuteResult方法中調用。如果FileDownloadName屬性不為空,意味著會采用附件的形式進行文件響應,FileResult會在重寫的ExecuteResult方法中進行Content-Disposition響應報頭的設置。如下麵的代碼片斷基本上體現了ExecuteResult方法在FileResult中的實現。
1: public abstract class FileResult : ActionResult
2: {
3: //其他成員
4: public override void ExecuteResult(ControllerContext context)
5: {
6: HttpResponseBase response = context.HttpContext.Response;
7: response.ContentType = this.ContentType;
8: if (!string.IsNullOrEmpty(this.FileDownloadName))
9: {
10: //生成Content-Disposition響應報頭值
11: string headerValue = ContentDispositionUtil.GetHeaderValue(this.FileDownloadName);
12: context.HttpContext.Response.AddHeader("Content-Disposition", headerValue);
13: }
14: this.WriteFile(response);
15: }
16: }
ASP.NET MVC定義了三個具體的FileResult,分別是FileContentResult、FilePathResult和FileStreamResult,接下來我們對它們進行單獨介紹。
FileContentResult是針對文件內容創建的FileResult。如下麵的代碼片斷所示,FileContentResult具有一個字節數組類型的隻讀屬性FileContents表示響應文件的內容,該屬性在構造函數中指定。FileContentResult針對文件內容的響應實現也很簡單,從如下所示的WriteFile方法定義可以看出,它隻是調用當前HttpResponse的OutputStream屬性的Write方法直接將表示文件內容的字節數組寫入響應輸出流。
1: public class FileContentResult : FileResult
2: {
3: public byte[] FileContents { get; }
4: public FileContentResult(byte[] fileContents, string contentType) ;
5:
6: protected override void WriteFile(HttpResponseBase response)
7: {
8: response.OutputStream.Write(this.FileContents, 0, this.FileContents.Length);
9: }
10: }
11:
12: public abstract class Controller : ControllerBase, ...
13: {
14: // 其他成員
15: protected FileContentResult File(byte[] fileContents, string contentType);
16: protected virtual FileContentResult File(byte[] fileContents, string contentType, string fileDownloadName);
17: }
抽象類Controller中定義了如上兩個File重載根據指定的字節數組、媒體類型和下載文件名(可選)生成相應的FileContentResult。由於FileContentResult是根據字節數組創建的,當我們需要動態生成響應文件內容(而不是從物理文件中讀取)時,FileContentResult是一個不錯的選擇。
從名稱可以看出,FilePathResult是一個根據物理文件路徑創建FileResult。如下麵的代碼片斷所示,表示響應文件的路徑通過隻讀屬性FileName表示,該屬性在構造函數中被初始化。在實現的WriteFile方法中,FilePathResult直接將文件路徑作為參數調用當前HttpResponse的TransmitFile實現了針對文件內容的響應。抽象類Controller同樣定義了兩個File方法重載來根據文件路徑創建相應的FilePathResult。
1: public class FilePathResult : FileResult
2: {
3: public string FileName { get; }
4: public FilePathResult(string fileName, string contentType);
5:
6: protected override void WriteFile(HttpResponseBase response)
7: {
8: response.TransmitFile(this.FileName);
9: }
10: }
11:
12: public abstract class Controller : ControllerBase, ...
13: {
14: //其他成員
15: protected FilePathResult File(string fileName, string contentType);
16: protected virtual FilePathResult File(string fileName, string contentType, string fileDownloadName);
17: }
FileStreamResult允許我們通過一個用於讀取文件內容的流來創建FileResult。如下麵的代碼片斷所示,讀取文件流通過隻讀屬性FileStream表示,該屬性在構造函數中被初始化。在實現的WriteFile方法中,FileStreamResult通過指定的文件流讀取文件內容,並最終調用當前HttpResponse的OutputStream屬性的Write方法將讀取的內容寫入當前HTTP響應的輸出流中。抽象類Controller中同樣定義了兩個File方法重載根據文件讀取流創建相應的FileStreamResult。
1: public class FileStreamResult : FileResult
2: {
3: public Stream FileStream { get; }
4: public FileStreamResult(Stream fileStream, string contentType);
5:
6: protected override void WriteFile(HttpResponseBase response)
7: {
8: Stream outputStream = response.OutputStream;
9: using (this.FileStream)
10: {
11: byte[] buffer = new byte[0x1000];
12: while (true)
13: {
14: int count = this.FileStream.Read(buffer, 0, 0x1000);
15: if (count == 0)
16: {
17: return;
18: }
19: outputStream.Write(buffer, 0, count);
20: }
21: }
22: }
23: }
24:
25: public abstract class Controller : ControllerBase, ...
26: {
27: //其他成員
28: protected FileStreamResult File(Stream fileStream, string contentType);
29: protected virtual FileStreamResult File(Stream fileStream, string contentType, string fileDownloadName);
30: }
為了讓讀者對FileResult具有更加深刻地認識,我們通過一個實例來演示如何通過FileResult來對外發布圖片。在通過Visual Studio的ASP.NET MVC項目模板創建的空Web應用中,我們在根目錄下添加一個名為images的子目錄來存放發布的.jpg圖片,然後我們定義如下一個HomeController。
1: public class HomeController : Controller
2: {
3: public ActionResult Index()
4: {
5: return View();
6: }
7:
8: public ActionResult Image(string id)
9: {
10: string path = Server.MapPath("/images/" + id + ".jpg");
11: return File(path, "image/jpeg");
12: }
13: }
圖片的發布體現在Action方法Image上,表示圖片ID的參數同時作為圖片的文件名(不含擴展名)。在該方法中,我們根據圖片ID解析出對應文件的路徑後,直接調用File方法創建一個媒體類型為“image/jpeg”的FilePathResult。在Action方法Index中呈現的View定義如下,我們通過一個列表顯示6張圖片。基於圖片的<img>元素的src屬性指定的地址正是指向定義在HomeController的Action方法Image,指定的表示圖片ID的參數分別是001、002、…、006。
1: <html>
2: <head>
3: <title>Gallery</title>
4: <style type="text/css">
5: li{list-style-type:none; float:left; margin:10px 10px 0px 0px;}
6: img{width:100px; height:100px;}
7: </style>
8: </head>
9: <body>
10: <ul>
11: <li><img alt="001" src="@Url.Action("Image", new { id = "001" })"/></li>
12: <li><img alt="002" src="@Url.Action("Image", new { id = "002" })"/></li>
13: <li><img alt="003" src="@Url.Action("Image", new { id = "003" })"/></li>
14: <li><img alt="004" src="@Url.Action("Image", new { id = "004" })"/></li>
15: <li><img alt="005" src="@Url.Action("Image", new { id = "005" })"/></li>
16: <li><img alt="006" src="@Url.Action("Image", new { id = "006" })"/></li>
17:
18: </ul>
19: </body>
20: </html>
我們將6張.jpg圖片存放到/imges目錄下,並分別命名為001、002、…、006。直接運行程序之後這6張圖片會以如下圖所示的效果顯示在瀏覽器上。
了解ASP.NET MVC幾種ActionResult的本質:EmptyResult & ContentResult
了解ASP.NET MVC幾種ActionResult的本質:FileResult
了解ASP.NET MVC幾種ActionResult的本質:JavaScriptResult & JsonResult
了解ASP.NET MVC幾種ActionResult的本質:HttpStatusCodeResult & RedirectResult/RedirectToRouteResult
微信公眾賬號:大內老A
微博:www.weibo.com/artech
如果你想及時得到個人撰寫文章以及著作的消息推送,或者想看看個人推薦的技術資料,可以掃描左邊二維碼(或者長按識別二維碼)關注個人公眾號(原來公眾帳號蔣金楠的自媒體將會停用)。
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁麵明顯位置給出原文連接,否則保留追究法律責任的權利。
最後更新:2017-10-25 17:03:45