【问题标题】:Writing to Output Stream from Action从动作写入输出流
【发布时间】:2010-10-30 21:32:10
【问题描述】:

出于一些奇怪的原因,我想将 HTML 从控制器操作直接写入响应流。 (我理解MVC分离,但这是特例。)

我可以直接写入HttpResponse 流吗?在这种情况下,控制器操作应该返回哪个IView 对象?我可以返回“null”吗?

【问题讨论】:

    标签: asp.net-mvc


    【解决方案1】:

    您可以使用return Content(...);,如果我没记错的话,... 将是您想要直接写入输出流的内容,或者什么都不写。

    看看Controller上的Content方法:http://aspnet.codeplex.com/SourceControl/changeset/view/22907#266451

    还有ContentResulthttp://aspnet.codeplex.com/SourceControl/changeset/view/22907#266450

    【讨论】:

      【解决方案2】:

      是的,您可以直接写入响应。完成后,您可以调用 CompleteRequest() 并且您不需要返回任何内容。

      例如:

      // GET: /Test/Edit/5
      public ActionResult Edit(int id)
      {
      
          Response.Write("hi");
          HttpContext.ApplicationInstance.CompleteRequest();
      
          return View();     // does not execute!
      }
      

      【讨论】:

      • 你应该避免 Response.End() stevesmithblog.com/blog/…
      • 后来更新为使用 CompleteRequest()。
      • 将“return View()”替换为“return Content("")”可能有助于避免丢失视图的错误。但是这种方法安全吗?
      • 这不是一个好方法,因为这个方法完成后不会执行任何ActionFilter属性。
      【解决方案3】:

      编写您自己的操作结果。这是我的一个例子:

      public class RssResult : ActionResult
      {
          public RssFeed RssFeed { get; set; }
      
          public RssResult(RssFeed feed) {
              RssFeed = feed;
          }
      
          public override void ExecuteResult(ControllerContext context) {
              context.HttpContext.Response.ContentType = "application/rss+xml";
              SyndicationResourceSaveSettings settings = new SyndicationResourceSaveSettings();
              settings.CharacterEncoding = new UTF8Encoding(false);
              RssFeed.Save(context.HttpContext.Response.OutputStream, settings);
          }
      }
      

      【讨论】:

        【解决方案4】:

        我使用了一个派生自FileResult 的类来使用普通的 MVC 模式实现这一点:

        /// <summary>
        /// MVC action result that generates the file content using a delegate that writes the content directly to the output stream.
        /// </summary>
        public class FileGeneratingResult : FileResult
        {
            /// <summary>
            /// The delegate that will generate the file content.
            /// </summary>
            private readonly Action<System.IO.Stream> content;
        
            private readonly bool bufferOutput;
        
            /// <summary>
            /// Initializes a new instance of the <see cref="FileGeneratingResult" /> class.
            /// </summary>
            /// <param name="fileName">Name of the file.</param>
            /// <param name="contentType">Type of the content.</param>
            /// <param name="content">Delegate with Stream parameter. This is the stream to which content should be written.</param>
            /// <param name="bufferOutput">use output buffering. Set to false for large files to prevent OutOfMemoryException.</param>
            public FileGeneratingResult(string fileName, string contentType, Action<System.IO.Stream> content,bool bufferOutput=true)
                : base(contentType)
            {
                if (content == null)
                    throw new ArgumentNullException("content");
        
                this.content = content;
                this.bufferOutput = bufferOutput;
                FileDownloadName = fileName;
            }
        
            /// <summary>
            /// Writes the file to the response.
            /// </summary>
            /// <param name="response">The response object.</param>
            protected override void WriteFile(System.Web.HttpResponseBase response)
            {
                response.Buffer = bufferOutput;
                content(response.OutputStream);
            }
        }
        

        控制器方法现在是这样的:

        public ActionResult Export(int id)
        {
            return new FileGeneratingResult(id + ".csv", "text/csv",
                stream => this.GenerateExportFile(id, stream));
        }
        
        public void GenerateExportFile(int id, Stream stream)
        {
            stream.Write(/**/);
        }
        

        注意,如果关闭缓冲,

        stream.Write(/**/);
        

        变得非常缓慢。解决方案是使用 BufferedStream。这样做在一种情况下将性能提高了大约 100 倍。见

        Unbuffered Output Very Slow

        【讨论】:

        • 最佳答案 - 只需添加一次文件,然后使用灵活的委托参数在所有其他情况下重复使用此概念。
        • 注意如果你用这种方式写一个大文件,你可能会得到一个OutOfMemoryException。您可以通过关闭缓冲来解决。像这样向WriteFile() 添加一行:response.Buffer = false;
        • 不错的解决方案。 +1 随意滚动@EricJ。对这个答案的建议(并更新 xmldocs)。如果这有问题,请随时回滚。
        • @spender:我发现了一个警告......关闭缓冲会使输出非常变慢。添加 BufferedStream 解决了这个问题。我用该信息更新了答案。
        • 如果写入是异步完成并且WriteFile方法在请求完成之前返回,这是否有效?我通过谷歌搜索“FileResult”和“async”找不到任何东西。
        【解决方案5】:

        如果您不想派生自己的结果类型,您可以简单地写信给Response.OutputStream 并返回new EmptyResult()

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2010-10-02
          • 1970-01-01
          • 2018-05-25
          • 2013-04-03
          • 1970-01-01
          • 1970-01-01
          • 2015-01-09
          • 2012-04-02
          相关资源
          最近更新 更多