@Developer4993 是正确的,要在解析整个响应之前将数据发送到客户端,有必要将Flush 发送到响应流。然而,他们的回答对于DELETE 和Synchronized.StreamWriter 都有些不同寻常。此外,如果 I/O 是同步的,Asp.Net Core 3.x 将引发异常。
这是在 Asp.Net Core 3.1 中测试的:
[HttpGet]
public async Task Get()
{
Response.ContentType = "text/plain";
StreamWriter sw;
await using ((sw = new StreamWriter(Response.Body)).ConfigureAwait(false))
{
foreach (var item in someReader.Read())
{
await sw.WriteLineAsync(item.ToString()).ConfigureAwait(false);
await sw.FlushAsync().ConfigureAwait(false);
}
}
}
假设 someReader 正在迭代数据库结果或某些 I/O 流,其中包含大量您不想在发送前缓冲的内容,这将使用每个 FlushAsync() 将一段文本写入响应流.
出于我的目的,使用HttpClient 使用结果比浏览器兼容性更重要,但是如果您发送足够多的文本,您将看到 chromium 浏览器以流式方式使用结果。浏览器一开始似乎缓冲了一定的量。
这变得更有用的是最新的IAsyncEnumerable 流,您的源要么是时间密集型的,要么是磁盘密集型的,但有时会产生一点:
[HttpGet]
public async Task<EmptyResult> Get()
{
Response.ContentType = "text/plain";
StreamWriter sw;
await using ((sw = new StreamWriter(Response.Body)).ConfigureAwait(false))
{
await foreach (var item in GetAsyncEnumerable())
{
await sw.WriteLineAsync(item.ToString()).ConfigureAwait(false);
await sw.FlushAsync().ConfigureAwait(false);
}
}
return new EmptyResult();
}
您可以将await Task.Delay(1000) 扔到foreach 中以演示连续流式传输。
最后,@StephenCleary 的 FileCallbackResult 也与这两个示例的工作方式相同。来自Infrastructure 命名空间深处的FileResultExecutorBase 只是有点可怕。
[HttpGet]
public IActionResult Get()
{
return new FileCallbackResult(new MediaTypeHeaderValue("text/plain"), async (outputStream, _) =>
{
StreamWriter sw;
await using ((sw = new StreamWriter(outputStream)).ConfigureAwait(false))
{
foreach (var item in someReader.Read())
{
await sw.WriteLineAsync(item.ToString()).ConfigureAwait(false);
await sw.FlushAsync().ConfigureAwait(false);
}
}
outputStream.Close();
});
}