流式文本输出用于长时间运行的操作?

时间:2011-01-04 17:35:24

标签: asp.net-mvc asp.net-mvc-3

我有一些实用程序操作可以通过return Content("my text","text/plain")返回文本输出。

有时这些方法需要几分钟才能运行(即日志解析,数据库维护)。

我想修改我的操作方法,以便不是一次性返回所有输出,而是在文本准备就绪时将文本流式传输到客户端。

这是一个人为的例子:

public ActionResult SlowText()
{
    var sb = new System.Text.StringBuilder();
    sb.AppendLine("This happens quickly...");
    sb.AppendLine("Starting a slow 10 second process...");
    System.Threading.Thread.Sleep(10000);
    sb.AppendLine("All done with 10 second process!");
    return Content(sb.ToString(), "text/plain");
}

如上所述,此操作将在10秒后返回三行文本。我想要的是一种方法来保持响应流打开,并立即返回前两行,然后在10秒后返回第三行。

我记得10年前在Classic ASP 3.0中使用Response对象进行此操作。是否有官方的,MVC友好的方式来实现这一目标?

-

更新:在应用中使用Razor .cshtml;但是没有使用任何视图(只是ContentResult)来执行这些操作。

5 个答案:

答案 0 :(得分:5)

直接写入Response对象应该有效,但仅限于一些简单的情况。许多MVC功能依赖于输出编写器替换(例如部分视图,Razor视图引擎等),如果直接写入响应,则结果将无序。

但是,如果您不使用视图而是直接在控制器中写入,那么您应该没问题(假设您的操作未被调用为子操作)。

答案 1 :(得分:1)

我会完全跳过MVC控制器,因为你打算打破封装。在它的位置,我将使用一个简单的IHttpHandler实现,直接流式传输到上述输出流。

答案 2 :(得分:1)

如果进程花费的时间超过最初预期,则表示您自己暴露于浏览器超时。然后你没有办法恢复发生的事情/除非你实现一个单独的方法来提供有关长时间运行过程的信息。

鉴于您无论如何都想要其他方法,您可以启动一个长时间运行的进程并立即返回。让浏览器检查另一种方法,该方法提供有关长时间运行过程的最新信息。在上次我不得不这样做的时候,我保持简单,只需在返回视图之前设置控制器的刷新标题。

至于开始一个长时间运行的过程,你可以这样做:

// in the controller class
delegate void MyLongProcess(); 
//...
// in the method that starts the action
MyLongProcess processTask = new MyLongProcess(_someInstance.TheLongRunningImplementation);
processTask.BeginInvoke(new AsyncCallback(EndMyLongProcess), processTask);
//...
public void EndMyLongProcess(IAsyncResult result)
{
   try{
      MyLongProcess processTask = (MyLongProcess)result.AsyncState;
      processTask.EndInvoke(result);
      // anything you needed at the end of the process
   } catch(Exception ex) {
      // an error happened, make sure to log this 
      // as it won't hit the global.asax error handler                    
   }
}

至于你在哪里记录所发生的行为的日志,由你决定你想要它的生存时间。它可以像静态字段/类一样简单,您可以在其中添加正在进行的进程的信息,或者将其保存到数据存储中,以便它可以在应用程序回收中存活。

以上假设这是一个长期运行的过程,用于报告已完成的操作。流媒体是一个不同的主题,但上述可能仍然在保持控制器和操作中的操作中起作用。只有负责在流程结果中传输客户端可用的内容的部分。

答案 3 :(得分:1)

您可以实现自定义ActionResult,如ContentStreamingResult,并在ExecuteResult方法中使用HttpContext,HttpRequest和HttpResponse。

public class ContentStreamingResult : ActionResult
    {
        private readonly TextReader _reader;

        public ContentStreamingResult(TextReader reader)
        {
            _reader = reader;
        }

        public override void ExecuteResult(ControllerContext context)
        {
            var httpContext = context.HttpContext;
            //Read text from the reader and write to the response
        }
    }

public class YourController : Controller
    {
        public ContentStreamingResult DownloadText()
        {
            string text = "text text text";
            return new ContentStreamingResult(new System.IO.StringReader(text));
        }
    }

答案 4 :(得分:0)

尝试Response.FlushBufferOutputfalse。请注意,它可以使用不同的操作结果,您必须直接写入response对象。您可以将它与AsyncController结合使用。