如何复制流

本文关键字:复制 何复制 | 更新日期: 2023-09-27 18:04:51

目前我有这个代码

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方法查找原始流返回到开始处更有效。

如果你不想缓冲并且不能查找,你需要将流内容按块推入两个消费者。读一个块,写两次

如果你还需要一个拉模型(即手一个可读流的一些组件),它会变得非常困难,线程涉及。您需要编写一个推拉适配器,这总是很困难。