来自 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】:如果你先调用LoadIntoBufferAsync
,ReadAsStreamAsync
返回一个只读 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】:
如果您先调用LoadIntoBufferAsync
,CopyToAsync
可用于填充只读 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 无需复制的主要内容,如果未能解决你的问题,请参考以下文章