【问题标题】:Stream.CopyToAsync is empty after first iteration第一次迭代后 Stream.CopyToAsync 为空
【发布时间】:2020-07-01 16:00:39
【问题描述】:
  • 背景:我需要将请求的内容中继到多个其他服务器(通过client.SendAsync(request))。
  • 问题:第一次请求后内容流为空
[HttpPost]
public async Task<IActionResult> PostAsync() {

    for (var n = 0; n <= 1; n++) {
        using (var stream = new MemoryStream()) {
            await Request.Body.CopyToAsync(stream);
            // why is stream.length == 0 in the second iteration? 
        }
    } 

    return StatusCode((int)HttpStatusCode.OK);
}

【问题讨论】:

  • 有一些发帖规则改变了,或者为什么我对这个问题投了反对票?

标签: c# .net http stream


【解决方案1】:

我发现CopyToAsync 函数将原始流位置设置为最后读取位置。下次我使用CopyToAsync 时,流会从上次读取位置开始读取,并且找不到更多内容。但是我不能使用Request.Body.Position = 0,因为它不受支持。我最终再次复制了流,并在每次复制后重置了位置。

如果有人知道更清洁的解决方案,欢迎指出。

using (var contentStream = new MemoryStream()) {
    await Request.Body.CopyToAsync(contentStream);                 

    for (var n = 0; n <= 1; n++) {
        using (var stream = new MemoryStream()) {
            contentStream.Position = 0;
            await contentStream.CopyToAsync(stream);

            // works
        }
    }
}

【讨论】:

  • 简而言之,就是我在您面前发布的内容。 :P
【解决方案2】:

流有一个指针,指示流在哪个位置;复制后,指针在末尾。您需要通过将其位置设置为 0 来回退流。

但这仅在支持搜索的流中受支持。你可以read the request stream only once。这是因为它是“从网络上”读取的,因此不支持搜索。

当您想将请求流复制到多个输出流时,您有两种选择:

  • 一边阅读一边转发
  • 一次读入内存,然后随意转发

第一个选项意味着所有前锋都以相同的速度发生;整个传输与输入一样慢,或者与最慢的阅读器一样慢。您从调用者那里读取了一个块,并将该块转发到所有转发地址。

对于第二种方法,您需要评估是否可以在内存中保存整个请求正文以及每个转发地址的正文。如果预计这不会是一个问题并且正确配置了合理的限制,那么只需将请求流复制到单个 MemoryStream 并在每次调用后复制并倒回该流:

using (var bodyStream = new MemoryStream()) 
{
    await Request.Body.CopyToAsync(bodyStream);

    for (...) 
    {
        using (var stream = new MemoryStream()) 
        {
            await bodyStream.CopyToAsync(stream);
            // Rewind for next copy
            bodyStream.Position = 0;
        }
    }
}

【讨论】:

  • 我想出了相同的解决方案,但我错过了这种行为的原因。谢谢你的解释:)
猜你喜欢
  • 2016-04-13
  • 2018-06-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-15
  • 1970-01-01
相关资源
最近更新 更多