繁体   English   中英

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

[英]How do you pass an OutputStream as an InputStream in C#?

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

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

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

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

MagicStreamReadingLibrary.ProcessStream(someInputStream);

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

如何将TransformingOutputStream到需要从输入Stream 读取的库函数?

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

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();
}

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

这是我刚刚汇总的内容,理论上应该可以工作(未经测试,我只知道它可以正确编译)。

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。

如果发现作家比读者快得多,则可能需要传递最大队列长度,这样写操作将阻塞,直到读者有机会阅读为止。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM