閱讀562 返回首頁    go 阿裏雲 go 技術社區[雲棲]


了解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張圖片會以如下圖所示的效果顯示在瀏覽器上。

image

了解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

  上一篇:go  了解ASP.NET MVC幾種ActionResult的本質:EmptyResult & ContentResult
  下一篇:go  了解ASP.NET MVC幾種ActionResult的本質:JavaScriptResult & JsonResult