2012-09-17 32 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

如果没有内容长度,浏览器将不会显示进度,因为它不知道要下载多少个字节,分块编码只是告诉客户端仍然有更多内容,但没有多少内容。所以如果你有大文件,并且浏览器只是继续接收数据块,它将永远不会显示进度%。 –