如何复制一个流

3

我目前有这段代码

SessionStream(Request.Content.ReadAsStreamAsync(), new { });

我需要以某种方式“镜像”传入的流,以便我有两个实例。 类似以下伪代码:
Task<Stream> stream = Request.Content.ReadAsStreamAsync();

SessionStream(stream, new { });

Stream theotherStram;

stream.Result.CopyToAsync(theotherStram)

ThoOtherStream(theotherStram, new { });

可能是在两个Stream实例之间复制的最佳方法的重复问题。 - Bogdan Maxim
1
@BogdanMaxim,这实际上并不好,因为“input”已经被使用了,你最终会得到两个并行运行的copyTo。仅仅搜索相似的单词是不够的,你应该在发布重复问题之前能够理解问题的主题... - user3077725
建议的重复并不重复。这个问题是关于将内容复制到两个流中,这要困难得多。 - usr
2个回答

3

一种总是有效的技术是将流复制到MemoryStream,然后使用它。

通常,更高效的方法是使用Seek方法将原始流定位回开头。这仅适用于支持寻址的流。

如果您不想缓冲并且无法寻址,则需要将流内容分块推送到两个消费者。读取一个块,写入两次。

如果您还需要一个拉模型(即将可读流交给某个组件),那么就会变得非常困难,并涉及线程。您需要编写一个推到拉适配器,这总是很困难的。


3
usr 的答案在2020年仍然正确,但对于那些想知道为什么这不是简单的问题的人,这里有一个简短的解释。
流的背后的思想是写入流和读取流是独立的。通常情况下,读取数据比写入数据要快得多(考虑通过网络接收数据 - 只要数据到达就可以立即读取数据),因此通常读取器等待新数据块,一旦到达,立即处理并删除它以释放内存,然后等待下一个数据块。
这使得可以处理潜在的无限数据流(例如应用程序日志流)而不使用太多RAM。
现在假设我们有2个读取器(根据问题所需)。一个数据块到达,然后我们必须等待两个读取器都读取完数据才能删除数据。这意味着必须将其存储在内存中,直到两个读取器完成为止。问题是读取者可以以非常不同的速度处理数据。例如,一个可以将其写入文件,另一个可以在内存中只计算符号。在这种情况下,要么快读者必须等待慢读者再读取,要么我们需要将数据保存到内存缓冲区中,并让读取器从中读取。在最坏的情况下,我们将在内存中获得完整的输入流的副本,基本上创建了一个内存流实例。
要实现第一种选项,您必须实现一个流读取器,该读取器知道您的流使用哪个更快,并考虑到它,相应地分发和删除数据。
如果您确定具有足够的内存并且处理速度不是关键,则只需使用内存流即可:
  using var memStream = new MemoryStream();
  await incomingStream.CopyToAsync(memStream);

  UseTheStreamForTheFirstTime(memStream);

  memStream.Seek(0, SeekOrigin.Begin);
  UseTheStreamAnotherTime(memStream);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接