2012-09-17 59 views
5

我想通過控制器ActionResult返回大文件,並已實現像下面的自定義FileResult類。FileResult緩衝到內存

public class StreamedFileResult : FileResult 
{ 
    private string _FilePath; 

    public StreamedFileResult(string filePath, string contentType) 
     : base(contentType) 
    { 
     _FilePath = filePath; 
    } 

    protected override void WriteFile(System.Web.HttpResponseBase response) 
    { 
     using (FileStream fs = new FileStream(_FilePath, FileMode.Open, FileAccess.Read)) 
     { 
      int bufferLength = 65536; 
      byte[] buffer = new byte[bufferLength]; 
      int bytesRead = 0; 

      while (true) 
      { 
       bytesRead = fs.Read(buffer, 0, bufferLength); 

       if (bytesRead == 0) 
       { 
        break; 
       } 

       response.OutputStream.Write(buffer, 0, bytesRead); 
      } 
     } 
    } 
} 

但是,我遇到的問題是整個文件似乎被緩衝到內存中。我需要做什麼來防止這種情況發生?

+1

爲什麼不使用現有的FileStreamResult? –

+1

我最初嘗試使用FileStreamResult,但它也將文件緩衝到內存中。 –

回答

8

您需要刷新響應以防止緩衝。但是,如果您在不設置內容長度的情況下繼續緩衝,用戶將看不到任何進度。因此,爲了讓用戶看到適當的進展,IIS會緩存整個內容,計算內容長度,應用壓縮併發送響應。我們已採用以下程序將文件傳送到客戶端,並獲得高性能。

FileInfo path = new FileInfo(filePath); 

// user will not see a progress if content-length is not specified 
response.AddHeader("Content-Length", path.Length.ToString()); 
response.Flush();// do not add anymore headers after this... 


byte[] buffer = new byte[ 4 * 1024 ]; // 4kb is a good for network chunk 

using(FileStream fs = path.OpenRead()){ 
    int count = 0; 
    while((count = fs.Read(buffer,0,buffer.Length)) >0){ 
     if(!response.IsClientConnected) 
     { 
      // network connection broke for some reason.. 
      break; 
     } 
     response.OutputStream.Write(buffer,0,count); 
     response.Flush(); // this will prevent buffering... 
    } 
} 

你可以改變緩衝區的大小,但是4KB是理想的低級別文件系統還讀取4KB的數據塊緩衝區。

+0

謝謝你,先生,這很好! –

0

Akash Kava部分是對的,部分是錯的。您不需要添加Content-Length標題或之後進行刷新。但是你需要定期刷新response.OutputStream然後response。 ASP.NET MVC(至少版本5)會自動將其轉換爲「Transfer-Encoding:chunked」響應。

byte[] buffer = new byte[ 4 * 1024 ]; // 4kb is a good for network chunk 

using(FileStream fs = path.OpenRead()){ 
    int count = 0; 
    while((count = fs.Read(buffer,0,buffer.Length)) >0){ 
     if(!response.IsClientConnected) 
     { 
      // network connection broke for some reason.. 
      break; 
     } 
     response.OutputStream.Write(buffer,0,count); 
     response.OutputStream.Flush(); 
     response.Flush(); // this will prevent buffering... 
    } 
} 

我測試了它,它工作。

+0

如果沒有內容長度,瀏覽器將不會顯示進度,因爲它不知道要下載多少個字節,分塊編碼只是告訴客戶端仍然有更多內容,但沒有多少內容。所以如果你有大文件,並且瀏覽器只是繼續接收數據塊,它將永遠不會顯示進度%。 –