ASP.NET MVC ActionResult本质:FileResult
FileResult是 一个基于文件的ActionResult,利用FileResult我们可以很容易地将从某个物理文件的内容响应给客户端。ASP.NET MVC定义了三个具体的FileResult,分别是FileContentResult、FilePathResult和 FileStreamResult。在这篇文章中我们将探讨三种具体的FileResult是如何将文件内容对请求进行响应的。
一、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
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
从名称可以看出,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
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具有更加深刻地认识,我们通过一个实例来演示如何通过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张图片会以如下图所示的效果显示在浏览器上。
作者:Artech
出处:http://artech.cnblogs.com/
加支付宝好友偷能量挖...