簡體   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