目前我有这个代码
SessionStream(Request.Content.ReadAsStreamAsync(), new { });
我需要以某种方式"镜像"传入流,以便我有它的两个实例。类似以下的伪代码:
Task<Stream> stream = Request.Content.ReadAsStreamAsync();
SessionStream(stream, new { });
Stream theotherStram;
stream.Result.CopyToAsync(theotherStram)
ThoOtherStream(theotherStram, new { });
usr的答案在2020年仍然是正确的,但对于那些想知道为什么它不是微不足道的人,这里有一个简短的解释。
流背后的思想是,对流的写入和从流中读取是独立的。通常,读的过程比写的要快得多(想想通过网络接收数据-你可以在数据到达时读取数据),所以通常读程序等待新的数据部分,在数据到达时立即处理它,然后将其丢弃以释放内存,然后等待下一部分。
这允许处理潜在的无限数据流(例如,应用程序日志流)而不使用太多RAM。
假设现在我们有2个读者(根据问题的要求)。数据部分到达,然后我们必须等待两个读取器读取数据,然后才能删除它。这意味着它必须存储在内存中,直到两个读取器都使用它。问题是阅读器处理数据的速度差别很大。例如,一个可以将其写入文件,另一个可以计算内存中的符号。在这种情况下,要么快的读取器必须等待慢的读取器才能继续读取,要么我们需要将数据保存到内存中的缓冲区中,然后让读取器从中读取。在最坏的情况下,我们将以内存中输入流的完整副本结束,基本上是创建内存流的实例。
要实现第一个选项,您必须实现一个流读取器,它知道您的流使用哪个更快,并考虑它,将相应地分发和丢弃数据。
如果您确定您有足够的内存,并且处理速度不是关键,只需使用内存流:
using var memStream = new MemoryStream();
await incomingStream.CopyToAsync(memStream);
UseTheStreamForTheFirstTime(memStream);
memStream.Seek(0, SeekOrigin.Begin);
UseTheStreamAnotherTime(memStream);
一种总是有效的技术是将流复制到MemoryStream
,然后使用它。
通常,使用Seek
方法查找原始流返回到开始处更有效。
如果你不想缓冲并且不能查找,你需要将流内容按块推入两个消费者。读一个块,写两次
如果你还需要一个拉模型(即手一个可读流的一些组件),它会变得非常困难,线程涉及。您需要编写一个推拉适配器,这总是很困难。