如何创建可以转换流的流包装流

时间:2015-02-07 14:00:35

标签: c# stream

var incomingStream = ...
var outgoingStream = ...

await incomingStream.CopyToAsync(outgoingStream);

上面的代码很简单,并将传入的流复制到outgoign流。这两个流都是分组的传输进出传递。

现在,让我说我想用Func<Stream,Stream,Task>之类的东西来转换流,如果不读取所有数据,我该怎么做呢。

因为我可以做到

var ms = new MemoryStream();
incomingStream.CopyTo(ms);

--- do transform of streams and seek
ms.CopyTo(outgoingStream)

但那会读到ms中的漏洞,是否有任何构建内容允许我从传入流中读取并写入不会缓冲所有内容的新流,而只是为缓冲数据保留一个小的内部流并且在数据再次被删除之前它不会从传入流中读取。

我想做的是:

    protected async Task XmlToJsonStream(Stream instream, Stream outStream)
    {
        XmlReaderSettings readerSettings = new XmlReaderSettings();
        readerSettings.IgnoreWhitespace = false;
        var reader = XmlReader.Create(instream, readerSettings);
        var jsonWriter = new JsonTextWriter(new StreamWriter(outStream));
        jsonWriter.WriteStartObject();

        while (await reader.ReadAsync())
        {
            jsonWriter.writeReader(reader);
        }
        jsonWriter.WriteEndObject();
        jsonWriter.Flush();
    }
    protected async Task XmlFilterStream(Stream instream, Stream outStream)
    {
        XmlReaderSettings readerSettings = new XmlReaderSettings();
        readerSettings.IgnoreWhitespace = false;
        var reader = XmlReader.Create(instream, readerSettings);
        var writer = XmlWriter.Create(outStream, new XmlWriterSettings { Async = true, CloseOutput = false })

        while (reader.Read())
        {
            writer.writeReader(reader);
        }


    }

但我不知道如何把它搞定。

var incomingStream = ...
var outgoingStream = ...
var temp=...  
XmlFilterStream(incomingStream,temp);
XmlToJsonStream(temp,outgoingstream);

因为如果我使用MemoryStream作为临时值,那么它最终是否会将它全部存储在流中。寻找在读取数据时再次丢弃数据的流。

以上所有内容仅仅是示例代码,缺少一些处理和寻找原因,但我希望我能说明我的目标。为了能够在仅复制流之间进行即插即用的设置,进行xml过滤并将其转换为json。

2 个答案:

答案 0 :(得分:2)

Streams是 bytes 的序列,因此流转换类似于Func<ArraySegment<byte>, ArraySegment<byte>>。然后,您可以以流媒体方式应用它:

async Task TransformAsync(this Stream source, Func<ArraySegment<byte>, ArraySegment<byte>> transform, Stream destination, int bufferSize = 1024)
{
  var buffer = new byte[bufferSize];
  while (true)
  {
    var bytesRead = await source.ReadAsync(buffer, 0, bufferSize);
    if (bytesRead == 0)
      return;
    var bytesToWrite = transform(new ArraySegment(buffer, 0, bytesRead));
    if (bytesToWrite.Count != 0)
      await destination.WriteAsync(bytesToWrite.Buffer, bytesToWrite.Offset, bytesToWrite.Count);
  }
}

它比这复杂一点,但这是一般的想法。它需要一些逻辑来确保WriteAsync写入所有字节;并且通常还有&#34; flush&#34;除了transform方法之外还需要的方法,该方法在源流完成时调用,因此转换算法最后有机会返回其最终数据以写入输出流。

如果您想要其他内容的流,例如XML或JSON类型,那么您最好使用Reactive Extensions

答案 1 :(得分:0)

我不确定我是否完全理解你的问题,但我认为你问的是如何在输入流上操作而不先将其完全加载到内存中。

在这种情况下,你不希望做这样的事情:

var ms = new MemoryStream();
incomingStream.CopyTo(ms);

将整个输入流incomingStream加载到内存中 - ms

从我所看到的情况来看,您的XmlFilterStream方法似乎是多余的,即XmlToJsonStream执行XmlFilterStream所做的所有事情。

为什么不只是:

protected async Task XmlToJsonStream(Stream instream, Stream outStream)
{
    XmlReaderSettings readerSettings = new XmlReaderSettings();
    readerSettings.IgnoreWhitespace = false;
    var reader = XmlReader.Create(instream, readerSettings);
    var jsonWriter = new JsonTextWriter(new StreamWriter(outStream));
    jsonWriter.WriteStartObject();

    while (await reader.ReadAsync())
    {
        jsonWriter.writeReader(reader);
    }
    jsonWriter.WriteEndObject();
    jsonWriter.Flush();
}

并称之为:

var incomingStream = ...
var outgoingStream = ...
XmlToJsonStream(incomingStream ,outgoingstream);

如果答案是您在XmlFilterStream中省略了一些重要的细节,那么在没有看到这些细节的情况下,我建议您只将这些细节集成到一个XmlToJsonStream函数中。