【问题标题】:Is there a built-in way to handle multiple files as one stream?是否有一种内置方法可以将多个文件作为一个流处理?
【发布时间】:2018-05-27 23:30:29
【问题描述】:

我有一个文件列表,我需要将它们按特定顺序读取到给定大小的字节 [] 中。对于单个文件,这本身不是问题,一个简单的 while ((got = fs.Read(piece, 0, pieceLength)) > 0) 可以完美地完成工作。文件的最后一部分可能比预期的要小,这很好。

现在,有一个棘手的问题:如果我有多个文件,我需要有一个连续的流,这意味着如果文件的最后一段比pieceLength 小,那么我需要读取(pieceLength-got)下一个文件,然后继续直到最后一个文件结束。

所以本质上,给定 X 个文件,我总是会读取长度正好是pieceLength 的片段,除了最后一个文件的最后一个片段,它可能更小。

我只是想知道 .net (3.5 SP1) 中是否已经有一些东西可以解决问题。我目前的方法是创建一个获取文件列表的类,然后公开一个Read(byte[] buffer, long index, long length) 函数,类似于 FileStream.Read()。这应该很简单,因为我不必更改读取数据的调用代码,但在我重新发明轮子之前,我只想再次检查轮子是否尚未内置到 BCL 中。

谢谢:)

【问题讨论】:

    标签: c# .net


    【解决方案1】:

    我不相信框架中有任何东西,但我建议让它更灵活一些 - 在你的构造函数中使用 IEnumerable<Stream>,并自己从 Stream 派生。然后要获取文件流,您可以(假设 C# 3.0)这样做:

    Stream combined = new CombinationStream(files.Select(file => File.Open(file));
    

    这里的“所有权”部分有点棘手 - 以上将允许组合流获得它读取的任何流的所有权,但您可能不希望它必须遍历所有其余的流,如果 过早关闭,则将它们全部关闭。

    【讨论】:

    • 这实际上是一个很好的方法,我只是不确定是否要实现自己的流。代替 IEnumerable 也可以工作,因为这样我的 CombinationStream 就可以完全控制“内部流”。我会考虑哪种方法最适合我。
    【解决方案2】:

    这是我根据@jon skeet 的想法提出的。

    它只是实现了 Read 这对我来说已经足够了。 (但我不需要帮助来实现 BeginRead/EndRead 方法。)这是包含同步和异步的完整代码 - 读取和 BeginRead/EndRead

    https://github.com/facebook-csharp-sdk/combination-stream/blob/master/src/CombinationStream-Net20/CombinationStream.cs

    internal class CombinationStream : System.IO.Stream
    {
        private readonly System.Collections.Generic.IList<System.IO.Stream> _streams;
        private int _currentStreamIndex;
        private System.IO.Stream _currentStream;
        private long _length = -1;
        private long _postion;
    
        public CombinationStream(System.Collections.Generic.IList<System.IO.Stream> streams)
        {
            if (streams == null)
            {
                throw new System.ArgumentNullException("streams");
            }
    
            _streams = streams;
            if (streams.Count > 0)
            {
                _currentStream = streams[_currentStreamIndex++];
            }
        }
    
        public override void Flush()
        {
            if (_currentStream != null)
            {
                _currentStream.Flush();
            }
        }
    
        public override long Seek(long offset, System.IO.SeekOrigin origin)
        {
            throw new System.InvalidOperationException("Stream is not seekable.");
        }
    
        public override void SetLength(long value)
        {
            this._length = value;
        }
    
        public override int Read(byte[] buffer, int offset, int count)
        {
            int result = 0;
            int buffPostion = offset;
    
            while (count > 0)
            {
                int bytesRead = _currentStream.Read(buffer, buffPostion, count);
                result += bytesRead;
                buffPostion += bytesRead;
                _postion += bytesRead;
    
                if (bytesRead <= count)
                {
                    count -= bytesRead;
                }
    
                if (count > 0)
                {
                    if (_currentStreamIndex >= _streams.Count)
                    {
                        break;
                    }
    
                    _currentStream = _streams[_currentStreamIndex++];
                }
            }
    
            return result;
        }
    
        public override long Length
        {
            get
            {
                if (_length == -1)
                {
                    _length = 0;
                    foreach (var stream in _streams)
                    {
                        _length += stream.Length;
                    }
                }
    
                return _length;
            }
        }
    
        public override long Position
        {
            get { return this._postion; }
            set { throw new System.NotImplementedException(); }
        }
        public override void Write(byte[] buffer, int offset, int count)
        {
            throw new System.InvalidOperationException("Stream is not writable");
        }
    
        public override bool CanRead
        {
            get { return true; }
        }
    
        public override bool CanSeek
        {
            get { return false; }
        }
    
        public override bool CanWrite
        {
            get { return false; }
        }
    }
    

    也可作为 NuGet 包使用

    Install-Package CombinationStream
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-11-26
      • 2012-05-04
      • 1970-01-01
      • 1970-01-01
      • 2016-05-09
      • 2012-10-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多