来自 HttpContent 的 MemoryStream 无需复制

Posted

技术标签:

【中文标题】来自 HttpContent 的 MemoryStream 无需复制【英文标题】:MemoryStream from HttpContent without copying 【发布时间】:2020-06-30 04:03:56 【问题描述】:

我正在尝试将 System.Net.Http 用于 POST 请求。我可以接受 HTTP 响应正文在内存中,但需要为其获取 MemoryStream。一种方法是调用 HttpContent.GetAsByteArrayAsync() 并在其上包装一个 MemoryStream,但我认为这需要将内容复制到一个单独的字节数组中(因为它返回字节 [] 的任务)。

如果响应正文已经在 HttpContent 的某个内部缓冲区中,是否可以在该缓冲区之上创建 MemoryStream,或者以某种方式从 HttpContent 返回 MemoryStream 并避免复制到单独的字节数组?

还有 HttpContent.GetAsStreamAsync(),但它返回的是常规 Stream,而不是 MemoryStream。尽管它可能已经是 MemoryStream 的一个实例,但我认为将返回的流转换为 MemoryStream 是不安全的,也不是一种好的做法? (因为这是可能改变的实现细节)。

有没有其他方法可以做到这一点,还是我别无选择,只能先复制到 byte[] 中?

谢谢。

【问题讨论】:

【参考方案1】:

如果你先调用LoadIntoBufferAsyncReadAsStreamAsync 返回一个只读 MemoryStream

await req.Content.LoadIntoBufferAsync();
var stream = (MemoryStream) await req.Content.ReadAsStreamAsync();

【讨论】:

谢谢,这似乎是正确的方法,但是我找不到提到 ReadAsStreamAsync 将在调用 LoadIntoBufferAsync 后返回只读 MemoryStream 的文档。微软有没有关于这方面的文档或示例? 我认为没有文档。如果您不信任该实现,您可以使用ReadAsByteArray() 并使用此构造函数public MemoryStream (byte[] buffer, bool writable); 创建一个MemoryStream。这就是HttpContent 在内部所做的。而且它不会复制缓冲区。 有源代码顺便说一句github.com/microsoft/referencesource/blob/master/System/net/… 谢谢,是的,从源代码来看,它确实返回了内存流。看起来这是在这种情况下最好的方法,但不确定这在未来的版本中是否可靠。我想确定我可以调用 ReadAsStreamAsync() 并检查实例是否为 MemoryStream,如果不是,则将返回的流复制到新的 MemoryStream 中。我认为 ReadAsByteArrayAsync 会复制缓冲区(至少在它调用 MemoryStream.ToArray() 进行复制的源代码中)。 你可以有一个单元测试覆盖铸造位。如果您升级到新版本,则此行为已更改。你的单元测试会失败,你会知道的。【参考方案2】:

如果您先调用LoadIntoBufferAsyncCopyToAsync 可用于填充只读 MemoryStream

var stream = new MemoryStream(req.Content.Headers.ContentLength);
await req.Content.LoadIntoBufferAsync((int)req.Content.Headers.ContentLength);
await req.Content.CopyToAsync(stream);

此实现不依赖于副作用,所有框架版本的文档都支持:https://docs.microsoft.com/en-us/dotnet/api/system.net.http.httpcontent.loadintobufferasync?view=netframework-4.6.2

注意:我尝试编辑上述答案,但无法做到……所以你来了。

【讨论】:

以上是关于来自 HttpContent 的 MemoryStream 无需复制的主要内容,如果未能解决你的问题,请参考以下文章

找不到如何使用 HttpContent

使用 StreamReader 读取 HttpContent 流直到字符限制

将 HttpContent 转换为 byte[]

常用的httpcontent

如何从 HttpContent 检索 JSON 数据

提供的“HttpContent”实例无效。它没有带有“边界”参数的“多部分”内容类型标头