18

Is there any way to use Stream.CopyTo to copy only certain number of bytes to destination stream? what is the best workaround?

Edit:
My workaround (some code omitted):

internal sealed class Substream : Stream 
    {
        private readonly Stream stream; 
        private readonly long origin;   
        private readonly long length; 
        private long position;        

        public Substream(Stream stream, long length)
        {
            this.stream = stream;
            this.origin = stream.Position;
            this.position = stream.Position;
            this.length = length;            
        }

public override int Read(byte[] buffer, int offset, int count)
        {
            var n = Math.Max(Math.Min(count, origin + length - position), 0);                
            int bytesRead = stream.Read(buffer, offset, (int) n);
            position += bytesRead;
            return bytesRead;            
        }
}

then to copy n bytes:

var substream = new Substream(stream, n);
                substream.CopyTo(stm);
morpheus
  • 18,676
  • 24
  • 96
  • 159

2 Answers2

19

The implementation of copying streams is not overly complicated. If you want to adapt it to copy only a certain number of bytes then it shouldn't be too difficult to tweak the existing method, something like this

public static void CopyStream(Stream input, Stream output, int bytes)
{
    byte[] buffer = new byte[32768];
    int read;
    while (bytes > 0 && 
           (read = input.Read(buffer, 0, Math.Min(buffer.Length, bytes))) > 0)
    {
        output.Write(buffer, 0, read);
        bytes -= read;
    }
}

The check for bytes > 0 probably isn't strictly necessary, but can't do any harm.

Community
  • 1
  • 1
Justin
  • 84,773
  • 49
  • 224
  • 367
  • 3
    For support of streams above 4GB, you should simply change the int parameter to a long parameter, then cast it to an int in the Math.Min function. – Trevor Elliott Sep 09 '13 at 19:14
6

What's wrong with just copying the bytes you need using a buffer?

long CopyBytes(long bytesRequired, Stream inStream, Stream outStream)
{
    long readSoFar = 0L;
    var buffer = new byte[64*1024];
    do
    {
        var toRead = Math.Min(bytesRequired - readSoFar, buffer.Length);
        var readNow = inStream.Read(buffer, 0, (int)toRead);
        if (readNow == 0)
            break; // End of stream
        outStream.Write(buffer, 0, readNow);
        readSoFar += readNow;
    } while (readSoFar < bytesRequired);
    return readSoFar;
}
Eli Algranti
  • 8,707
  • 2
  • 42
  • 50
  • I wrapped your code in a function to make it more clear, you can roll it back if you don't want it. – Scott Chamberlain Oct 23 '12 at 00:41
  • I think I prefer my implementation but I still upvoted this one because it essentially does the same thing :) – Justin Oct 23 '12 at 00:56
  • Justin - your implementation is good, I like the while which does away with the break, even if it does add an extra compare at the start (pedantic I know). I like mine because it keeps track of the total number of bytes copied which might be important if they are not the same as bytesRequired. Just change the type 'bytes' in your function to 'long' for completion. Upvoted :) – Eli Algranti Oct 23 '12 at 03:43
  • Thanks @henon. There was a missing ';' and I was missing a cast to int. Fixed now. – Eli Algranti May 23 '16 at 06:05