ASP.NET MVC 瞭解FileResult的本質

来源:http://www.cnblogs.com/weiweixiang/archive/2016/07/14/5667355.html
-Advertisement-
Play Games

FileResult是一個基於文件的ActionResult,利用FileResult我們可以很容易地將從某個物理文件的內容響應給客戶端。ASP.NET MVC定義了三個具體的FileResult,分別是FileContentResult、FilePathResult和FileStreamResul ...


FileResult是一個基於文件的ActionResult,利用FileResult我們可以很容易地將從某個物理文件的內容響應給客戶端。ASP.NET MVC定義了三個具體的FileResult,分別是FileContentResultFilePathResultFileStreamResult。在這篇文章中我們將探討三種具體的FileResult是如何將文件內容對請求進行響應的。

一、FileResult

如下麵的代碼片斷所示,FileResult具有一個表示媒體類型的只讀屬性ContentType,該屬性在構造函數中被初始化。當我們基於某個物理文件創建相應的FileResult對象的時候應該根據文件的類型指定媒體類型,比如說目標文件是一個.jpg圖片,那麼對應的媒體類型為“image/jpeg”,對於一個.pdf文件,則採用“application/pdf”。

public abstract class FileResult : ActionResult
{
    private string _fileDownloadName;
        
    protected FileResult(string contentType)
    {
        if (string.IsNullOrEmpty(contentType))
        {
            throw new ArgumentException(MvcResources.Common_NullOrEmpty, "contentType");
        }
        this.ContentType = contentType;
    }
        
    public override void ExecuteResult(ControllerContext context)
    {
        if (context == null)
        {
            throw new ArgumentNullException("context");
        }
        HttpResponseBase response = context.HttpContext.Response;
        response.ContentType = this.ContentType;
        if (!string.IsNullOrEmpty(this.FileDownloadName))
        {
            string headerValue = ContentDispositionUtil.GetHeaderValue(this.FileDownloadName);
            context.HttpContext.Response.AddHeader("Content-Disposition", headerValue);
        }
        this.WriteFile(response);
    }
        
    protected abstract void WriteFile(HttpResponseBase response);
        
    public string ContentType { get; private set; }
        
    public string FileDownloadName
    {
        get
        {
            return (this._fileDownloadName ?? string.Empty);
        }
        set
        {
            this._fileDownloadName = value;
        }
    }
        
    internal static class ContentDispositionUtil
    {
        private const string HexDigits = "0123456789ABCDEF";
            
        private static void AddByteToStringBuilder(byte b, StringBuilder builder)
        {
            builder.Append('%');
            int num = b;
            AddHexDigitToStringBuilder(num >> 4, builder);
            AddHexDigitToStringBuilder(num % 0x10, builder);
        }
            
        private static void AddHexDigitToStringBuilder(int digit, StringBuilder builder)
        {
            builder.Append("0123456789ABCDEF"[digit]);
        }
            
        private static string CreateRfc2231HeaderValue(string filename)
        {
            StringBuilder builder = new StringBuilder("attachment; filename*=UTF-8''");
            foreach (byte num in Encoding.UTF8.GetBytes(filename))
            {
                if (IsByteValidHeaderValueCharacter(num))
                {
                    builder.Append((char) num);
                }
                else
                {
                    AddByteToStringBuilder(num, builder);
                }
            }
            return builder.ToString();
        }
            
        public static string GetHeaderValue(string fileName)
        {
            foreach (char ch in fileName)
            {
                if (ch > '\x007f')
                {
                    return CreateRfc2231HeaderValue(fileName);
                }
            }
            ContentDisposition disposition = new ContentDisposition {
                FileName = fileName
            };
            return disposition.ToString();
        }
            
        private static bool IsByteValidHeaderValueCharacter(byte b)
        {
            if ((0x30 <= b) && (b <= 0x39))
            {
                return true;
            }
            if ((0x61 <= b) && (b <= 0x7a))
            {
                return true;
            }
            if ((0x41 <= b) && (b <= 90))
            {
                return true;
            }
            switch (b)
            {
                case 0x3a:
                case 0x5f:
                case 0x7e:
                case 0x24:
                case 0x26:
                case 0x21:
                case 0x2b:
                case 0x2d:
                case 0x2e:
                    return true;
            }
            return false;
        }
    }
}
View Code

針對文件的響應具有兩種形式,即內聯(Inline)和附件(Attachment)。一般來說,前者會利用瀏覽器直接打開響應的文件,而後者會以獨立的文件下載到客戶端。對於後者,我們一般會為下載的文件指定一個文件名,這個文件名可以通過FileResult的FileDownloadName屬性來指定。文件響應在預設情況下採用內聯的方式,如果需要採用附件的形式,需要為響應創建一個名稱為Content-Disposition的報頭,該報頭值的格式為“attachment; filename={ FileDownloadName }”。

FileResult僅僅是一個抽象類,文件內容的輸出實現在抽象方法WriteFile中,該方法會在重寫的ExecuteResult方法中調用。如果FileDownloadName屬性不為空,意味著會採用附件的形式進行文件響應,FileResult會在重寫的ExecuteResult方法中進行Content-Disposition響應報頭的設置。如下麵的代碼片斷基本上體現了ExecuteResult方法在FileResult中的實現。

public override void ExecuteResult(ControllerContext context)
{
    if (context == null)
    {
        throw new ArgumentNullException("context");
    }
    HttpResponseBase response = context.HttpContext.Response;
    response.ContentType = this.ContentType;
    if (!string.IsNullOrEmpty(this.FileDownloadName))
    {
        string headerValue = ContentDispositionUtil.GetHeaderValue(this.FileDownloadName);
        context.HttpContext.Response.AddHeader("Content-Disposition", headerValue);
    }
    this.WriteFile(response);
}

ASP.NET MVC定義了三個具體的FileResult,分別是FileContentResult、FilePathResult和FileStreamResult,接下來我們對它們進行單獨介紹。

二、FileContentResult

FileContentResult是針對文件內容創建的FileResult。如下麵的代碼片斷所示,FileContentResult具有一個位元組數組類型的只讀屬性FileContents表示響應文件的內容,該屬性在構造函數中指定。FileContentResult針對文件內容的響應實現也很簡單,從如下所示的WriteFile方法定義可以看出,它只是調用當前HttpResponse的OutputStream屬性的Write方法直接將表示文件內容的位元組數組寫入響應輸出流。

public class FileContentResult : FileResult
{
    public FileContentResult(byte[] fileContents, string contentType) : base(contentType)
    {
        if (fileContents == null)
        {
            throw new ArgumentNullException("fileContents");
        }
        this.FileContents = fileContents;
    }
        
    protected override void WriteFile(HttpResponseBase response)
    {
        response.OutputStream.Write(this.FileContents, 0, this.FileContents.Length);
    }
        
    public byte[] FileContents { get; private set; }
}
public abstract class Controller : ControllerBase, IActionFilter, IAuthorizationFilter, IDisposable, IExceptionFilter, IResultFilter, IAsyncController, IController, IAsyncManagerContainer
{
    protected internal FileContentResult File(byte[] fileContents, string contentType)
    {
        return this.File(fileContents, contentType, null);
    }
    protected internal virtual FileContentResult File(byte[] fileContents, string contentType, string fileDownloadName)
    {
        return new FileContentResult(fileContents, contentType) { FileDownloadName = fileDownloadName };
    }
}

抽象類Controller中定義瞭如上兩個File重載根據指定的位元組數組、媒體類型和下載文件名(可選)生成相應的FileContentResult。由於FileContentResult是根據位元組數組創建的,當我們需要動態生成響應文件內容(而不是從物理文件中讀取)時,FileContentResult是一個不錯的選擇。

三、FilePathResult

從名稱可以看出,FilePathResult是一個根據物理文件路徑創建FileResult。如下麵的代碼片斷所示,表示響應文件的路徑通過只讀屬性FileName表示,該屬性在構造函數中被初始化。在實現的WriteFile方法中,FilePathResult直接將文件路徑作為參數調用當前HttpResponse的TransmitFile實現了針對文件內容的響應。抽象類Controller同樣定義了兩個File方法重載來根據文件路徑創建相應的FilePathResult。

public class FilePathResult : FileResult
{
    public FilePathResult(string fileName, string contentType) : base(contentType)
    {
        if (string.IsNullOrEmpty(fileName))
        {
            throw new ArgumentException(MvcResources.Common_NullOrEmpty, "fileName");
        }
        this.FileName = fileName;
    }
        
    protected override void WriteFile(HttpResponseBase response)
    {
        response.TransmitFile(this.FileName);
    }
        
    public string FileName { get; private set; }
}
public abstract class Controller : ControllerBase,...
{
    protected internal FilePathResult File(string fileName, string contentType)
    {
        return this.File(fileName, contentType, null);
    }
    protected internal virtual FilePathResult File(string fileName, string contentType, string fileDownloadName)
    {
        return new FilePathResult(fileName, contentType) { FileDownloadName = fileDownloadName };
    }
    .....
} 

四、FileStreamResult

FileStreamResult允許我們通過一個用於讀取文件內容的流來創建FileResult。如下麵的代碼片斷所示,讀取文件流通過只讀屬性FileStream表示,該屬性在構造函數中被初始化。在實現的WriteFile方法中,FileStreamResult通過指定的文件流讀取文件內容,並最終調用當前HttpResponse的OutputStream屬性的Write方法將讀取的內容寫入當前HTTP響應的輸出流中。抽象類Controller中同樣定義了兩個File方法重載根據文件讀取流創建相應的FileStreamResult。

public class FileStreamResult : FileResult
{
    private const int BufferSize = 0x1000;
        
    public FileStreamResult(Stream fileStream, string contentType) : base(contentType)
    {
        if (fileStream == null)
        {
            throw new ArgumentNullException("fileStream");
        }
        this.FileStream = fileStream;
    }
        
    protected override void WriteFile(HttpResponseBase response)
    {
        Stream outputStream = response.OutputStream;
        using (this.FileStream)
        {
            byte[] buffer = new byte[0x1000];
            while (true)
            {
                int count = this.FileStream.Read(buffer, 0, 0x1000);
                if (count == 0)
                {
                    return;
                }
                outputStream.Write(buffer, 0, count);
            }
        }
    }
        
    public Stream FileStream { get; private set; }
}
public abstract class Controller : ControllerBase, ...
{
    protected internal FileStreamResult File(Stream fileStream, string contentType)
    {
        return this.File(fileStream, contentType, null);
    }
    protected internal virtual FileStreamResult File(Stream fileStream, string contentType, string fileDownloadName)
    {
        return new FileStreamResult(fileStream, contentType) { FileDownloadName = fileDownloadName };
    }
    ...
}

 

本文轉載自蔣老師的瞭解ASP.NET MVC幾種ActionResult的本質:FileResult


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 說是手機充值系統有點裝了,其實就是調用了聚合數據的支付介面,其實挺簡單的事 但是我發現博客園竟然沒有類似文章,我就個出頭鳥把我的代碼貢獻出來吧 首先說準備工作: 去聚合數據申請賬號-添加手機支付的認證-認證通過後為賬戶充值。 上述工作完成後,開始準備開發要用到的必要參數: appid:在個人中心-我 ...
  • 就在去年Insus.NET已經寫好的一個WebAPI項目,並且發佈在IIS中。參考《創建與使用Web API》http://www.cnblogs.com/insus/p/5019088.html 從上面的鏈接可以查看到那篇實例。今天Insus.NET就另開一個ASP.NET MVC項目,去操作這個 ...
  • 最近看書比較多,正好對過去幾年的軟體開發做個總結。寫這個的初衷只是為了簡單的做一些記錄。 前言 複雜的應用程式總是面臨很多的頁面之間的數據交互,怎樣創建松耦合的程式一直是多數工程師所思考的問題。諸如依賴註入,PubSub模式,MVVM等概念,都致力於幫助我們創建更加松耦合易於維護的程式,也有不少框架 ...
  • 模擬Visual Studio中的完全匹配查找 轉載請註明出處:http://www.cnblogs.com/jzblogs/p/5670397.html ...
  • 1,document.getElementById getElementById是通過Id來設置/返回HTML標簽的屬性及調用其事件與方法。用這個方法基本上可以控制頁面所有標簽,條件很簡單,就是給每個標簽分配一個ID號。返回具有指定ID屬性值的第一個對象的一個引用。 語法: var inTag = ...
  • 第一步:購買功能變數名稱、伺服器、DNS解析這裡我們是在準備做一個網站的原材料,這三樣缺一不可。目前來說,可以租用的虛擬伺服器的商家很多,像亞馬遜、騰訊、阿裡等,當然,你也可以去找國外的一些免費的伺服器來用。實際一點來說,亞馬遜的伺服器你在成功註冊後有一年的試用期,這個倒是挺不錯的,不過他需要國外的一個信用... ...
  • 原文地址:http://docode.top/Article/Detail/10003 目錄: 1、.Net(C#)平臺下Des加密解密源代碼 2、.Net(C#)平臺下Aes加密解密源代碼 3、.Net(C#)平臺下Sha1加密解密源代碼 4、.Net(C#)平臺下MD5加密解密源代碼 5、總結 ...
  • webapi問世已久,稀里糊塗的人哪它都當mvc來使,畢竟已mvc使用級別的經驗就可以應對webapi。 webapi和mvc在asp.net5時代合體了,這告訴我們,其實 它倆還是有區別的,要不現在也不會取兩個名字,但是由於本人歸納總結能力較差,各種不同也無法一一列出了。 在webapi中 Hel ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...