Bob
Bob

Reputation: 99814

Creating a byte array from a stream

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

Here is my current solution with .NET 3.5.

Stream s;
byte[] b;

using (BinaryReader br = new BinaryReader(s))
{
    b = br.ReadBytes((int)s.Length);
}

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

Upvotes: 1122

Views: 1225092

Answers (19)

sultan s. alfaifi
sultan s. alfaifi

Reputation: 324

MemoryStream stream = new MemoryStream();
// do what you want to save in stream buffer
//  ...
// then define byte array with specific size same as stream length.
byte[] readByte = new byte[stream.Length];
// copy all byte from stream to an byte array
readByte = stream.ToArray();

Upvotes: 2

adsamcik
adsamcik

Reputation: 1335

If a stream supports the Length property, a byte array can be directly created. The advantage is that MemoryStream.ToArray creates the array twice. Plus, probably some unused extra bytes in the buffer. This solution allocates the exact array needed. If the stream does not support the Length property, it will throw NotSupportedException exception.

It is also worth noting that arrays cannot be bigger than int.MaxValue.

public static async Task<byte[]> ToArrayAsync(this Stream stream)
{
    var array = new byte[stream.Length];
    await stream.ReadAsync(array, 0, (int)stream.Length);
    return array;
}

Complete code which switches between both versions based on whether the stream supports seeking or not. It includes checks for Position and unreliable Length. That might slightly reduce speed. In my tests ToArrayAsyncDirect is about 3 times faster compared to ToArrayAsyncGeneral.

public static class StreamExtensions
{
    public static readonly byte[] TempArray = new byte[4];

    /// <summary>
    /// Converts stream to byte array.
    /// </summary>
    /// <param name="stream">Stream</param>
    /// <param name="cancellationToken">Cancellation token</param>
    /// <returns>Stream data as array</returns>
    /// <returns>Binary data from stream in an array</returns>
    public static async Task<byte[]> ToArrayAsync(this Stream stream, CancellationToken cancellationToken)
    {
        if (!stream.CanRead)
        {
            throw new AccessViolationException("Stream cannot be read");
        }

        if (stream.CanSeek)
        {
            return await ToArrayAsyncDirect(stream, cancellationToken);
        }
        else
        {
            return await ToArrayAsyncGeneral(stream, cancellationToken);
        }
    }

    /// <summary>
    /// Converts stream to byte array through MemoryStream. This doubles allocations compared to ToArrayAsyncDirect.
    /// </summary>
    /// <param name="stream">Stream</param>
    /// <param name="cancellationToken">Cancellation token</param>
    /// <returns></returns>
    private static async Task<byte[]> ToArrayAsyncGeneral(Stream stream, CancellationToken cancellationToken)
    {
        using MemoryStream memoryStream = new MemoryStream();
        await stream.CopyToAsync(memoryStream, cancellationToken);
        return memoryStream.ToArray();
    }

    /// <summary>
    /// Converts stream to byte array without unnecessary allocations.
    /// </summary>
    /// <param name="stream">Stream</param>
    /// <param name="cancellationToken">Cancellation token</param>
    /// <returns>Stream data as array</returns>
    /// <exception cref="ArgumentException">Thrown if stream is not providing correct Length</exception>
    private static async Task<byte[]> ToArrayAsyncDirect(Stream stream, CancellationToken cancellationToken)
    {
        if (stream.Position > 0)
        {
            throw new ArgumentException("Stream is not at the start!");
        }


        var array = new byte[stream.Length];
        int bytesRead = await stream.ReadAsync(array, 0, (int)stream.Length, cancellationToken);

        if (bytesRead != array.Length ||
            await stream.ReadAsync(TempArray, 0, TempArray.Length, cancellationToken) > 0)
        {
            throw new ArgumentException("Stream does not have reliable Length!");
        }

        return array;
    }
}

Upvotes: 7

Egemen &#199;iftci
Egemen &#199;iftci

Reputation: 744

You can use this extension method.

public static class StreamExtensions
{
    public static byte[] ToByteArray(this Stream stream)
    {
        var bytes = new List<byte>();

        int b;

        // -1 is a special value that mark the end of the stream
        while ((b = stream.ReadByte()) != -1)
            bytes.Add((byte)b);

        return bytes.ToArray();
    }
}

Upvotes: 2

Kirk Woll
Kirk Woll

Reputation: 77606

Since there's no modern (i.e. async) version of this answer, this is the extension method I use for this purpose:

public static async Task<byte[]> ReadAsByteArrayAsync(this Stream source)
{
    // Optimization
    if (source is MemoryStream memorySource)
        return memorySource.ToArray();

    using var memoryStream = new MemoryStream();
    await source.CopyToAsync(memoryStream);
    return memoryStream.ToArray();
}

The optimization is based on the fact the source code for ToArray calls some internal methods.

Upvotes: 5

&#246;nder &#231;albay
&#246;nder &#231;albay

Reputation: 183

Combinig two of the most up-voted answers into an extension method:

public static byte[] ToByteArray(this Stream stream)
{
    if (stream is MemoryStream)
        return ((MemoryStream)stream).ToArray();
    else
    {
        using MemoryStream ms = new();
        stream.CopyTo(ms);
        return ms.ToArray();
    }            
}

Upvotes: 11

Jon Skeet
Jon Skeet

Reputation: 1503080

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 - and before .NET 4 - 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();
    }
}

With .NET 4 and above, I'd use Stream.CopyTo, which is basically equivalent to the loop in my code - create the MemoryStream, call stream.CopyTo(ms) and then return ms.ToArray(). Job done.

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).

Upvotes: 1478

Nilesh Kumar
Nilesh Kumar

Reputation: 315

You can simply use ToArray() method of MemoryStream class, for ex-

MemoryStream ms = (MemoryStream)dataInStream;
byte[] imageBytes = ms.ToArray();

Upvotes: 23

Nathan Phillips
Nathan Phillips

Reputation: 12455

While Jon's answer is correct, he is rewriting code that already exists in CopyTo. So for .Net 4 use Sandip's solution, but for previous version of .Net use Jon's answer. Sandip's code would be improved by use of "using" as exceptions in CopyTo are, in many situations, quite likely and would leave the MemoryStream not disposed.

public static byte[] ReadFully(Stream input)
{
    using (MemoryStream ms = new MemoryStream())
    {
        input.CopyTo(ms);
        return ms.ToArray();
    }
}

Upvotes: 917

Wieslaw Olborski
Wieslaw Olborski

Reputation: 335

In namespace RestSharp.Extensions there is method ReadAsBytes. Inside this method is used MemoryStream and there is the same code like in some examples on this page but when you are using RestSharp this is easiest way.

using RestSharp.Extensions;
var byteArray = inputStream.ReadAsBytes();

Upvotes: 4

Fred.S
Fred.S

Reputation: 74

This is the function which I am using, tested and worked well. please bear in mind that 'input' should not be null and 'input.position' should reset to '0' before reading otherwise it will break the read loop and nothing will read to convert to array.

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

            return temp;
        }
    }

Upvotes: 4

SensorSmith
SensorSmith

Reputation: 1230

In case anyone likes it, here is a .NET 4+ only solution formed as an extension method without the needless Dispose call on the MemoryStream. This is a hopelessly trivial optimization, but it is worth noting that failing to Dispose a MemoryStream is not a real failure.

public static class StreamHelpers
{
    public static byte[] ReadFully(this Stream input)
    {
        var ms = new MemoryStream();
        input.CopyTo(ms);
        return ms.ToArray();
    }
}

Upvotes: 8

Kalyn Padayachee
Kalyn Padayachee

Reputation: 41

Create a helper class and reference it anywhere you wish to use it.

public static class StreamHelpers
{
    public static byte[] ReadFully(this Stream input)
    {
        using (MemoryStream ms = new MemoryStream())
        {
            input.CopyTo(ms);
            return ms.ToArray();
        }
    }
}

Upvotes: 4

Abba
Abba

Reputation: 549

i was able to make it work on a single line:

byte [] byteArr= ((MemoryStream)localStream).ToArray();

as clarified by johnnyRose, Above code will only work for MemoryStream

Upvotes: -7

Fernando Neira
Fernando Neira

Reputation: 4044

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);
    }
}

Upvotes: 141

A. Wheatman
A. Wheatman

Reputation: 6396

just my couple cents... the practice that I often use is to organize the methods like this as a custom helper

public static class StreamHelpers
{
    public static byte[] ReadFully(this Stream input)
    {
        using (MemoryStream ms = new MemoryStream())
        {
            input.CopyTo(ms);
            return ms.ToArray();
        }
    }
}

add namespace to the config file and use it anywhere you wish

Upvotes: 57

NothinRandom
NothinRandom

Reputation: 235

The one above is ok...but you will encounter data corruption when you send stuff over SMTP (if you need to). I've altered to something else that will help to correctly send byte for byte: '

using System;
using System.IO;

        private static byte[] ReadFully(string input)
        {
            FileStream sourceFile = new FileStream(input, FileMode.Open); //Open streamer
            BinaryReader binReader = new BinaryReader(sourceFile);
            byte[] output = new byte[sourceFile.Length]; //create byte array of size file
            for (long i = 0; i < sourceFile.Length; i++)
                output[i] = binReader.ReadByte(); //read until done
            sourceFile.Close(); //dispose streamer
            binReader.Close(); //dispose reader
            return output;
        }'

Upvotes: 5

Michal T
Michal T

Reputation: 814

You can even make it fancier with extensions:

namespace Foo
{
    public static class Extensions
    {
        public static byte[] ToByteArray(this Stream stream)
        {
            using (stream)
            {
                using (MemoryStream memStream = new MemoryStream())
                {
                     stream.CopyTo(memStream);
                     return memStream.ToArray();
                }
            }
        }
    }
}

And then call it as a regular method:

byte[] arr = someStream.ToByteArray()

Upvotes: 14

Brian Hinchey
Brian Hinchey

Reputation: 3691

I get a compile time error with Bob's (i.e. the questioner's) code. Stream.Length is a long whereas BinaryReader.ReadBytes takes an integer parameter. In my case, I do not expect to be dealing with Streams large enough to require long precision, so I use the following:

Stream s;
byte[] b;

if (s.Length > int.MaxValue) {
  throw new Exception("This stream is larger than the conversion algorithm can currently handle.");
}

using (var br = new BinaryReader(s)) {
  b = br.ReadBytes((int)s.Length);
}

Upvotes: 9

Sandip Patel
Sandip Patel

Reputation: 787

MemoryStream ms = new MemoryStream();
file.PostedFile.InputStream.CopyTo(ms);
var byts = ms.ToArray();
ms.Dispose();

Upvotes: 77

Related Questions