views:

9746

answers:

4

What is the preffered method for creating a byte array from an input stream?

Here is my current solution with .NET 3.5.

Is it still a better idea to read and write chunks of the stream?

Stream s;
byte[] b;
using (BinaryReader br = new BinaryReader(s)) {
 b = br.ReadBytes(s.Length);
}
+39  A: 

It really depends on whether or not you can trust s.Length. For many streams, you just don't know how much data there will be. In such cases, I'd use code like this:

public static byte[] ReadFully(Stream input)
{
    byte[] buffer = new byte[16*1024];
    using (MemoryStream ms = new MemoryStream())
    {
        int read;
        while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
        {
            ms.Write(buffer, 0, read);
        }
        return ms.ToArray();
    }
}

EDIT: I should perhaps explain why my answer is longer than the others. Stream.Read doesn't guarantee that it will read everything it's asked for. If you're reading from a network stream, for example, it may read one packet's worth and then return, even if there will be more data soon. BinaryReader.Read will keep going until the end of the stream or your specified size, but you still have to know the size to start with.

The above method will keep reading (and copying into a MemoryStream) until it runs out of data. It then asks the MemoryStream to return a copy of the data in an array. If you know the size to start with - or think you know the size, without being sure - you can construct the MemoryStream to be that size to start with. Likewise you can put a check at the end, and if the length of the stream is the same size as the buffer (returned by MemoryStream.GetBuffer) then you can just return the buffer. So the above code isn't quite optimised, but will at least be correct. It doesn't assume any responsibility for closing the stream - the caller should do that.

See this article for more info (and an alternative implementation).

Jon Skeet
@Jon: if you know the exact length of your stream, is it ok to use the code the poster wrote?
Pure.Krome
@Pure: Using BinaryReader, yes - that will loop round until the stream is empty or enough data has been read. Don't try to use Stream that way though.
Jon Skeet
@Jon, it may be worth mentioning http://www.yoda.arachsys.com/csharp/readbinary.html
Sam Saffron
A: 

I personally use

    public byte[] GetArray(Stream s, int maxSize)
    {
        byte[] array = new byte[maxSize];
        s.Read(array, 0, (int)array.Length);
        s.Close();
        return array;
    }

You obviously need to know what is the maximum size you expect.

Be cautious: maxSize apparently needs to be lesser than 10k (see comments)

Luk
... and assume that the single Read call read all the data?... and not inform the client as to how much data was actually read?... and fail if there's more than 10K in the stream?... and sometimes leave the stream open, and sometimes not? (Exceptions!)
Jon Skeet
this function si used in a try/catch/finally statement and don't need to know that, but that's a very good point
Luk
You should not close streams you dont own/open/instantiate.
leppie
maxSize doesn't need to be less than 10K - the problem with the previous code was (IIRC - I can't see that edit now) the stream's length was being used, and that could be more than 10K. The above still has the problem that the client has no idea how much data has actually been read.
Jon Skeet
@Jon Skeet: Nope I don't think I changed the code -beside replacing the hardcoded value by MaxSize) (I use this on a non seekable stream, I'm not sure stream.length would work)
Luk
A: 

There is a slightly more efficient way of doing this on this site.

Sam Saffron
A: 

Just want to point out that in case you have a MemoryStream you already have memorystream.ToArray() for that.

Also, if you are dealing with streams of unknown or different subtypes and you can receive a MemoryStream, you can relay on said method for those cases and still use the accepted answer for the others, like this:

    public static byte[] StreamToByteArray(Stream stream)
    {
        if (stream is MemoryStream)
        {
            return ((MemoryStream)stream).ToArray();                
        }
        else
        {
            // Jon Skeet's accepted answer 
            return ReadFully(stream);
        }
    }
Maxmalin
Huh, what are all the upvotes for? Even with the most generous assumptions, this only works for streams that are already `MemoryStream`s. Of course the example is also obviously incomplete, in how it's using an uninitialized variable.
romkyns
That's right, thanks for pointing that out. The point still stands for MemoryStream though, so I fixed it to reflect that.
Maxmalin