Andrew
Andrew

Reputation: 129

Partial stream of FileStream

I have 30 GB FileStream. I want to get 2 (or more) Streams from it, beginning (0-50%) and the end (50%-100%). If I use MemoryStream and CopyTo(), it will consume 30 GB of RAM, while I just need new separated streams. Is this possible?

Upvotes: 1

Views: 1084

Answers (3)

someone
someone

Reputation: 13

I've further improved upon the file and implemented the Seek function upon the work @Shamork.Fu did

using System;
using System.IO;

namespace DataStoreFramework
{
    public class PartialReadStream : Stream
    {
        private readonly Stream _stream;
        private readonly long _streamStart;
        private readonly long _streamLength;
        private readonly long _streamEnd;
        private readonly long _streamEndOffset;
        private readonly long _streamMaxEndOffset;

        public PartialReadStream(Stream stream,long offset, long size)
        {
            _stream = stream;
            _streamStart = offset;
            _streamLength = size;
            _streamEnd = offset + size;
            _streamEndOffset = _streamEnd - _stream.Length;
            _streamMaxEndOffset = _streamLength + _stream.Length - _streamEnd;

            if (_stream.CanSeek)  {
                _stream.Seek(offset, SeekOrigin.Begin);
            }
            else {
                _stream.Position = offset;
            }
        }

        public override bool CanRead => _stream.CanRead;

        public override bool CanSeek => _stream.CanSeek;

        public override bool CanWrite => false;

        public override long Length => Math.Min((_stream.Length - _streamStart), _streamLength);

        public override long Position
        {
            get => _stream.Position - _streamStart;
            set => _stream.Position = _streamStart + value;
        }

        public override void Flush() {
            throw new NotImplementedException();
        }

        public override int Read(byte[] buffer, int offset, [NonNegativeValue]int count)
        {
            var p = _stream.Position;

            if (p < _streamStart) {
                _stream.Position = _streamStart;
            }

            if (p > _streamEnd) {
                return 0;
            }

            if (p + count > _streamEnd) {
                count = (int)(_streamEnd - p);
            }

            return _stream.Read(buffer, offset, count);
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            long seekValue = 0;

            switch (origin)
            {
                case SeekOrigin.Begin:
                    if (offset < 0) {
                        throw new IOException("IO.IO_SeekBeforeBegin");
                    }
                    seekValue = _streamStart + offset;
                    break;

                case SeekOrigin.End:
                    seekValue = _streamEndOffset + offset;
                    if (seekValue < -_streamMaxEndOffset) {
                        throw new IOException("IO.IO_SeekBeforeBegin");
                    }
                    break;

                case SeekOrigin.Current:
                    seekValue = offset;
                    if (_stream.Position + seekValue < 0)  {
                        throw new IOException("IO.IO_SeekBeforeBegin");
                    }
                    break;
            }

            return _stream.Seek(seekValue, origin) - _streamStart;
        }

        public override void SetLength(long value) =>
            throw new NotImplementedException();

        public override void Write(byte[] buffer, int offset, int count) =>
            throw new NotImplementedException();

    }
}

Upvotes: 0

Shamork.Fu
Shamork.Fu

Reputation: 53

impovements for SegmentedStream by @Andrew

public class PartialStream : Stream
{
    public Stream Stream { get; private set; }
    public long StreamStart { get; private set; }
    public long StreamLength { get; private set; }
    public long StreamEnd{get; private set;}

    public PartialStream(Stream stream, long offset, long size)
    {
        Stream = stream;
        StreamStart = offset;
        StreamLength = size;
        StreamEnd=offset+size;
        stream.Seek(offset, SeekOrigin.Begin);
    }

    public override bool CanRead => true;

    public override bool CanSeek => false;

    public override bool CanWrite => false;

    public override long Length => Math.Min((Stream.Length - StreamStart), StreamLength);

    public override long Position
    {
        get => Stream.Position - StreamStart;
        set => Stream.Position = StreamStart + value;
    }

    public override void Flush()
    {
        throw new NotImplementedException();
    }

    public override int Read(byte[] buffer, int offset, int count)
    {
        var p=Stream.Position;
        if (p < StreamStart)
        {
            Stream.Position = StreamStart;
        }
        if (p > StreamEnd)//EOF
        {
            return 0;
        }
        if (p + count > StreamEnd)
        {
            count = (int)(StreamEnd-p);
        }
        return Stream.Read(buffer, offset, count);
    }

    public override long Seek(long offset, SeekOrigin origin)
    {
        //Seek will be complicated as there are three origin types.
        //you can do it yourself
        throw new NotImplementedException();
    }

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

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

Upvotes: 4

Andrew
Andrew

Reputation: 129

I made own Stream for this task. Not tested well but looks okay

class SegmentedStream : Stream
    {
        Stream Stream { get; set; }
        long StreamStart { get; set; }
        long StreamLength { get; set; }

        public SegmentedStream(Stream stream, long position, long size)
        {
            Stream = stream;
            StreamStart = position;
            StreamLength = size;
        }

        public override bool CanRead => true;

        public override bool CanSeek => true;

        public override bool CanWrite => false;

        public override long Length => Math.Min((Stream.Length - StreamStart), StreamLength);

        public override long Position { get => Stream.Position - StreamStart; set => Stream.Position = StreamStart + value; }

        public override void Flush()
        {
            throw new NotImplementedException();
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            return Stream.Read(buffer, offset, count);
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            return Stream.Seek(StreamStart + offset, origin);
        }

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

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

Upvotes: 2

Related Questions