C# 是ASP.NET MVC';s FileStreamResult的效率比直接写入响应输出流要低,还是我遗漏了什么?

C# 是ASP.NET MVC';s FileStreamResult的效率比直接写入响应输出流要低,还是我遗漏了什么?,c#,asp.net-mvc,stream,C#,Asp.net Mvc,Stream,首先,我喜欢ASP.NETMVC。这个问题不是对它的批评。相反,我想确认我所看到的,并确保我没有错过什么。请容忍我。。。如果不提供一点背景,我就无法回答这个问题 这个问题与在流中返回数据以响应HTTP Post有关。在ASP.NET MVC之前的旧时代,您可以通过将数据直接管道化到响应流中来实现这一点。例如,您可以执行以下操作: someObjectThatDumpsOutputToWhateverStreamYouHandIt.WriteTo(Response.OutputStream);

首先,我喜欢ASP.NETMVC。这个问题不是对它的批评。相反,我想确认我所看到的,并确保我没有错过什么。请容忍我。。。如果不提供一点背景,我就无法回答这个问题

这个问题与在流中返回数据以响应HTTP Post有关。在ASP.NET MVC之前的旧时代,您可以通过将数据直接管道化到响应流中来实现这一点。例如,您可以执行以下操作:

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

但是,它对单元测试不是很友好。事实上,您仍然可以在ASP.NET MVC中编写此类代码,但自定义的方法是避免这样做。相反,ASP.NET MVC会鼓励您返回ActionResult。ActionResult是一个ASP.NETMVC概念,它的存在主要是为了单元测试友好。它允许你“声明”你想做什么。单元测试可以执行控制器操作并确认它获得了预期的操作结果。该单元测试可以在浏览器之外运行

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

然而,问题就在这里,我的问题的基础是:如果您让控制器方法返回一个FileStreamResult,并将一个要返回的流交给它,那么您似乎再也无法将数据直接转储到响应流中了。现在,似乎您被迫使用备份存储(如内存或文件)创建自己的流,将数据转储到其中,然后将该流交给返回的FileStreamResult

因此,我似乎必须这样做(故意省略dispose/using/etc):

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

我的问题是:我是否忽略了什么,或者说使用FileStreamResult强制您拥有一个中间存储位置是准确的,如果您要直接写入响应的输出流,您不会被迫拥有这个位置?


谢谢。

我认为更准确的说法是,假设您处理的每个流都有一个对象dumpsoutput,而它不是从System.IO.Stream继承的,那么使用FileStreamResult会迫使您

a) 为从System.IO.Stream继承的DumpSoutputtoWheverStream对象实现包装器,或

b) 使用临时MemoryStream实例

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


按问题编辑提问者:我选择这个问题作为公认的答案。两个回答都很有帮助。我必须选择一个,这一个指向Phil Haack的DelegatingActionResult,这正是我所需要的。

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

当您可能已经有一个流时,有几个实例:

  • 存储在SQL 2008
    FILESTREAM
    列中的数据
  • 直接从另一个端点转发的数据(
    NetworkStream
  • 用于发送压缩内容的
    GzipStream
    deflatesttream
  • 从命名管道(
    PipeStream
    )发送
  • ……等等
FileStreamResult
(据我所知)的主要用例是当您有一个预先存在的流,需要从中读取,然后写回响应;
FileStreamResult
为您处理缓冲区,而不必自己进行读写和计算

因此,简短的回答是否定的,
FileStreamResult
不一定强制您拥有一个“中间”存储位置,如果流是您的数据源。如果数据已经在内存中或磁盘上的某个地方,准备好并等待直接写入响应流,我就不会为
FileStreamResult
而烦恼了


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

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

这为您提供了所需的单元可测试性,并让y
MemoryStream myIntermediateStream = new MemoryStream();
someObjectThatDumpsOutputToWhateverStreamYouHandIt.WriteTo(myIntermediateStream ); 

return new FileStreamResult(myIntermediateStream, "application/pdf");