5

我正在尝试通过控制器 ActionResult 返回大文件,并实现了一个自定义 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);
            }
        }
    }
}

但是我遇到的问题是整个文件似乎被缓冲到内存中。我需要做什么来防止这种情况发生?

4

2 回答 2

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 的块读取缓冲区。

于 2012-09-17T17:47:42.677 回答
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...
   }
}

我测试了它并且它有效。

于 2016-04-04T18:32:36.177 回答