如何在.NET中拆分(复制)Stream?

时间:2009-06-28 21:53:59

标签: c# .net io stream

有谁知道我在哪里可以找到Stream splitter实现?

我正在寻找一个Stream,并获得两个独立的流,可以独立读取和关闭,而不会相互影响。这些流应该返回与原始流相同的二进制数据。无需实现职位或寻求等......仅向前。

我更喜欢它不只是将整个流复制到内存中并多次提供服务,这对于实现自己来说相当简单。

那里有什么可以做到的吗?

7 个答案:

答案 0 :(得分:4)

没有开箱即用。

您需要以FIFO方式缓冲原始流中的数据,仅丢弃所有“读取器”流已读取的数据。

我会用:

  • 一个“管理”对象,其中包含某种byte []队列,用于保存要缓冲的块,并在需要时从源流中读取其他数据
  • 一些“读者”实例知道他们正在读取的位置和缓冲区,并从“管理”请求下一个块,并在他们不再使用块时通知它,以便它可以从队列

答案 1 :(得分:3)

这可能会很棘手,而且不会将所有内容缓冲在内存中(如果流分别位于BOF和EOF)。

我想知道将流写入磁盘,复制并从磁盘读取两个流是不容易的,Close()内置自我删除(即编写自己的Stream围绕FileStream)包装。

答案 2 :(得分:2)

如果不重复至少部分资源流,你不可能真正做到这一点 - 主要是因为如果听起来不像你可以控制它们消耗的速度(多线程?)。你可以做一些聪明的事情,一个人阅读另一个人的读取(从而只在那个点上制作副本),但这种复杂的听起来似乎不值得麻烦。

答案 3 :(得分:2)

以下似乎有效,称为EchoStream http://www.codeproject.com/Articles/3922/EchoStream-An-Echo-Tee-Stream-for-NET 它是一个非常古老的实现(2003),但应提供一些上下文

通过Redirect writes to a file to a stream C#

找到

答案 4 :(得分:1)

我认为您无法找到通用实现来做到这一点。 Stream是相当抽象的,您不知道字节的来源。例如,你不知道它是否会支持寻求;而且你不知道操作的相对成本。 (Stream可能是从远程服务器读取数据的抽象,甚至是备份磁带的抽象!)。

如果您能够拥有MemoryStream并存储一次内容,则可以使用相同的缓冲区创建两个单独的流;它们将表现为独立的Streams但只使用一次内存。

否则,我认为你最好通过创建一个包装类来存储从一个流读取的字节,直到它们也被第二个流读取。这将为您提供所需的仅向前行为 - 但在最坏的情况下,如果在第一个Stream完成读取所有内容之前未读取第二个流,则可能存在将所有字节存储在内存中的风险。

答案 5 :(得分:1)

我已经在github和NuGet上提供了一个SplitStream。

就是这样。

using (var inputSplitStream = new ReadableSplitStream(inputSourceStream))

using (var inputFileStream = inputSplitStream.GetForwardReadOnlyStream())
using (var outputFileStream = File.OpenWrite("MyFileOnAnyFilestore.bin"))

using (var inputSha1Stream = inputSplitStream.GetForwardReadOnlyStream())
using (var outputSha1Stream = SHA1.Create())
{
    inputSplitStream.StartReadAhead();

    Parallel.Invoke(
        () => {
            var bytes = outputSha1Stream.ComputeHash(inputSha1Stream);
            var checksumSha1 = string.Join("", bytes.Select(x => x.ToString("x")));
        },
        () => {
            inputFileStream.CopyTo(outputFileStream);
        },
    );
}

我没有在非常大的流上测试它,但试一试。

github:https://github.com/microknights/SplitStream

答案 6 :(得分:-1)

随着async / await的引入,只要你的一个读取任务都是异步的,你应该能够只使用一个OS线程处理相同的数据两次。

我认为你想要的是你到目前为止看到的数据块的链接列表。然后,您可以拥有多个自定义Stream实例,其中包含指向此列表的指针。当块从列表的末尾掉落时,它们将被垃圾收集。立即重用内存需要一些其他类型的循环列表和引用计数。可行,但更复杂。

当您的自定义Stream可以从缓存中应答ReadAsync调用时,复制数据,将指针向下推进列表并返回。

当您的Stream已赶上缓存列表的末尾时,您希望在不等待它的情况下向基础流发出单个ReadAsync,并使用数据块缓存返回的Task。因此,如果任何其他Stream阅读器也会在此读取完成之前赶上并尝试阅读更多内容,则可以返回相同的Task对象。

这样,两个读者都会将等待继续挂钩到同一ReadAsync调用的结果。当单个读取返回时,两个读取任务将按顺序执行其过程的下一步。