ASP.NET MVC的FileStreamResult是否比直接写入响应输出流效率低,或者我错过了什么?

Cha*_*ers 9 c# asp.net-mvc stream

首先,我喜欢 ASP.NET MVC.这个问题不是批评它.相反,我想确认我认为我看到了什么,并确保我没有错过任何东西.忍受我......如果没有提供一点上下文,我无法回答这个问题.

问题与在Stream中返回数据以响应HTTP Post有关.在ASP.NET MVC之前的旧时代,您可以通过将数据直接传递到Response流来实现此目的.例如,您可能会执行以下操作:

someObjectThatDumpsOutputToWhateverStreamYouHandIt.WriteTo(Response.OutputStream); 
Run Code Online (Sandbox Code Playgroud)

请注意此代码的一个关键方面:我没有实现任何后备存储.我没有必要将信息流传输到字节数组,或将其转储到临时文件中.相反,ASP.NET已经设置了一个Stream,用于将响应传回浏览器,我已将我想要的输出直接转储到该Stream中.这在CPU,内存和执行时间方面比将所有数据复制到某个临时位置然后将其移动到Response流中更有效.

但是,它对单元测试不是很友好.实际上,您仍然可以在ASP.NET MVC中执行此类代码,但自定义将避免这种情况.相反,ASP.NET MVC会鼓励您返回ActionResult.ActionResult是一个ASP.NET MVC概念,主要用于单元测试友好.它允许您"声明"您想要做的事情.单元测试可以执行Controller操作并确认它获得了预期的ActionResult.该单元测试可以在浏览器外部运行.

ASP.NET MVC提供了一种用于返回数据流的ActionResult.它被称为FileStreamResult.不要让名字中的"文件"这个词欺骗你.它是关于返回数据流,正如我们上面所讨论的那样.

但是,这是问题所在,也是我的问题的基础:如果你让你的Controller方法返回一个FileStreamResult,然后你把它想要返回一个Stream,那么它似乎没有任何方法可以转储数据直接到Response流.现在,您似乎被迫使用后备存储(例如内存或文件)创建自己的Stream,将数据转储到其中,然后将该Stream传递给您返回的FileStreamResult.

所以看来我必须做这样的事情(故意省略dispose/using /等):

MemoryStream myIntermediateStream = new MemoryStream();
someObjectThatDumpsOutputToWhateverStreamYouHandIt.WriteTo(myIntermediateStream ); 

return new FileStreamResult(myIntermediateStream, "application/pdf");
Run Code Online (Sandbox Code Playgroud)

请注意,myIntermediateStream会将大数据流的内容临时存储在内存中,以便FileStreamResult稍后可以将其再次复制到Response输出流中.

所以这是我的问题:我是否忽略了某些内容,或者说使用FileStreamResult强制您拥有一个中间存储位置是否准确,如果您直接写入Response的输出流,您将不会被迫拥有这个位置?

谢谢.

Aar*_*ght 10

我认为背后的主要思想FileStreamResult是当你已经拥有一个流时使用它.如果你必须创建一个临时流来使用它,那么没有意义; 这就是为什么还有一个FilePathResultFileContentResult.

目前几种情况下,您可能已经有流:

  • 存储在SQL 2008 FILESTREAM列中的数据;
  • 数据直接从另一个端点转发(NetworkStream);
  • A GzipStreamDeflateStream用于发送压缩的内容;
  • 从命名管道(PipeStream)发送;
  • ...等等.

FileStreamResult(据我所知)的主要用例是当你有一个预先存在的流,你需要从中读取然后写回响应; 而不是自己进行阅读和写作以及计算缓冲,FileStreamResult而是为您处理.

所以简短的答案是否定的,FileStreamResult如果流是您的数据,则不一定会强迫您拥有"中间"存储位置.FileStreamResult如果数据已经在内存中或磁盘上,准备好并等待直接写入响应流,我就不会打扰了.


我想补充一点澄清:这个库的问题在于它颠倒了你真正需要的功能FileStreamResult.它不是向您提供您可以读取的流,而是希望您提供流以便它可以写入.请注意,这是一种常见的模式,但它并不适合这项任务.

如果流包含大量数据并且您不想用它来咀嚼内存,那么您应该能够使用PipeStream衍生物自行反转流.创建一个命名或匿名管道,创建一个服务器流(您可以使用所需的小缓冲区大小进行初始化)并将其提供给库,然后在同一个管道上创建一个客户端流并将其提供给FileStreamResult.

这为您提供了所需的单元可测试性,并允许您精确控制允许进程使用的内存量.它还具有能够异步生成数据的附加优势,如果您尝试丢弃数GB的数据,则可能需要这些数据.


Jos*_*eph 5

我相信更准确地说,假设someObjectThatDumpsOutputToWhateverStreamYouHandIt不从System.IO.Stream继承,那么使用FileStreamResult强制你要么

a)为从System.IO.Stream继承的someObjectThatDumpsOutputToWhateverStreamYouHandIt实现一个包装器,或者

b)使用临时的MemoryStream实例.

因此,它不一定效率较低,但似乎需要更多的工作才能返回一个值.

按问题编辑Asker:我选择这个问题作为接受的答案.这两个回复都非常有帮助.我必须选择一个,这个指向Phil Haack的DelegatingActionResult,这正是我所需要的.

  • 别客气.我只是环顾四周,找到了这个链接:http://haacked.com/archive/2008/05/10/writing-a-custom-file-download-action-result-for-asp.net-mvc.aspx.以防万一你已经没见过了.此外,看起来@Aaronaught还有另一种可能值得关注的解决方案.我之前没有听说过PipeStream. (2认同)