如何在C#中将OutputStream作为InputStream传递?

时间:2016-05-02 22:25:53

标签: c# io stream

我的目标是从一个流中读取,转换该流,并将其用作接受Stream读取的库的输入。

我正在使用两个不同的库。一个输出Stream并对其进行转换。我们称之为TransformingOutputStream。其预期用途是:

var outputStream = new TransformingOutputStream(finalDestinationOutputStream);
inputStream.CopyTo(outputStream);

我正在使用另一个接受输入Stream的库。它可以执行任何需要,然后从该流中读取。其预期用途是:

MagicStreamReadingLibrary.ProcessStream(someInputStream);

我无法将TransformingOutputStream传递给它,因为它的用途是写入,而不是读取。我无法控制任何一个库。

如何将TransformingOutputStream与输入Stream读取的库函数连接起来?

3 个答案:

答案 0 :(得分:2)

到目前为止,这是我使用匿名管道的最佳工作示例:

using( var pipeServer = new AnonymousPipeServerStream( PipeDirection.Out ) ) {
    var pipeServerTask = Task.Run(
        async () => {
            using( var stream = getInputStream() ) {
                await stream.CopyToAsync( new TransformingOutputStream( pipeServer ) );
            }
            pipeServer.WaitForPipeDrain();
            pipeServer.Dispose();
        } );

    using( var client = new AnonymousPipeClientStream( PipeDirection.In, pipeServer.ClientSafePipeHandle ) ) {
        MagicStreamReadingLibrary.ProcessStream( client );
    }
    pipeServerTask.Wait();
}

答案 1 :(得分:1)

将其写入平面文件,然后将其读回。

答案 2 :(得分:0)

这是我刚刚放在一起的东西,它应该在理论上工作(未经测试,我只知道它正确编译)。

public class BufferingStream
{
    private readonly Stream _readingStream;
    private readonly Stream _writingStream;
    private BlockingCollection<byte[]> _buffer;

    public BufferingStream()
    {
        _buffer = new BlockingCollection<byte[]>(new ConcurrentQueue<byte[]>());
        _readingStream = new InternalReadingStream(_buffer);
        _writingStream = new InternalWritingStream(_buffer);
    }

    public BufferingStream(int maxQueueLength)
    {
        _buffer = new BlockingCollection<byte[]>(new ConcurrentQueue<byte[]>(), maxQueueLength);
        _readingStream = new InternalReadingStream(_buffer);
        _writingStream = new InternalWritingStream(_buffer);
    }
    public Stream GetReadingStream()
    {
        return _readingStream;
    }

    public Stream GetWritingStream()
    {
        return _writingStream;
    }

    public int QueueLength
    {
        get { return _buffer.Count; }
    }

    public class InternalWritingStream : Stream
    {
        private readonly BlockingCollection<byte[]> _queuedBytes;

        public InternalWritingStream(BlockingCollection<byte[]> queuedBytes)
        {
            _queuedBytes = queuedBytes;
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            byte[] internalBuffer = new byte[count];
            Array.Copy(buffer, offset, internalBuffer, 0, count);
            _queuedBytes.Add(internalBuffer);
        }

        public override void Close()
        {
            _queuedBytes.CompleteAdding();
            base.Close();
        }

        public override void Flush()
        {
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            throw new NotSupportedException();
        }

        public override void SetLength(long value)
        {
            throw new NotSupportedException();
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            throw new NotSupportedException();
        }

        public override bool CanRead
        {
            get { return false; }
        }

        public override bool CanSeek
        {
            get { return false; }
        }

        public override bool CanWrite
        {
            get { return true; }
        }

        public override long Length
        {
            get { throw new NotSupportedException(); }
        }

        public override long Position
        {
            get { throw new NotSupportedException(); }
            set { throw new NotSupportedException(); }
        }
    }

    private sealed class InternalReadingStream : Stream
    {
        private readonly BlockingCollection<byte[]> _queuedBytes;
        private byte[] _currentItem;
        private int _currentItemOffset;

        public InternalReadingStream(BlockingCollection<byte[]> queuedBytes)
        {
            _queuedBytes = queuedBytes;
            _currentItem = new byte[0];
            _currentItemOffset = 0;
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            if (_currentItemOffset == _currentItem.Length)
            {
                //Try to take the next buffer, if we can't take a item it means we where done adding from the source.
                var taken = _queuedBytes.TryTake(out _currentItem, Timeout.Infinite);
                if (!taken)
                    return 0;

                _currentItemOffset = 0;
            }
            var bytesToRead = Math.Min(count, _currentItem.Length - _currentItemOffset);
            Array.Copy(_currentItem, _currentItemOffset, buffer, offset, bytesToRead);
            _currentItemOffset += bytesToRead;

            return bytesToRead;
        }

        public override void Flush()
        {
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            throw new NotSupportedException();
        }

        public override void SetLength(long value)
        {
            throw new NotSupportedException();
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            throw new NotSupportedException();
        }

        public override bool CanRead
        {
            get { return true; }
        }

        public override bool CanSeek
        {
            get { return false; }
        }

        public override bool CanWrite
        {
            get { return false; }
        }

        public override long Length
        {
            get { throw new NotSupportedException(); }
        }

        public override long Position
        {
            get { throw new NotSupportedException(); }
            set { throw new NotSupportedException(); }
        }
    }
}

它在用例中的工作方式是

var bufferingStream = new BufferingStream();

Task.Run(() =>
{
    using(var inputStream = GetTheStreamFromSomewhere();
    using(var finalDestinationOutputStream = bufferingStream.GetWritingStream())
    using(var outputStream = new TransformingOutputStream(finalDestinationOutputStream))
    {
        inputStream.CopyTo(outputStream);
    }
}

using(var someInputStream = bufferingStream.GetReadingStream()) //Technically a using is not necessary on the reading stream but it is good to keep good habits.
{
    MagicStreamReadingLibrary.ProcessStream(someInputStream);
}

最初拨打.Read(来电ProcessStream号码将会阻止,直到数据可用。当字节变为可用时.Read(取消阻塞并传递数据。处置finalDestinationOutputStream后,它会将队列标记为已完成添加,一旦outputStream完成其上次读取,它将在任何后续调用时返回0。

如果您发现您的作者比您的阅读器快得多,您可能希望传入最大队列长度,因此写入将阻止,直到读者有机会阅读。