views:

8762

answers:

6

Is there a simple way or method to convert an Stream into a byte[] in C#?

+7  A: 

Call next function like

byte[] m_Bytes = StreamHelper.ReadToEnd (mystream);

Function:

public static byte[] ReadToEnd(System.IO.Stream stream)
    {
        long originalPosition = stream.Position;
        stream.Position = 0;  

        try
        {
            byte[] readBuffer = new byte[4096];

            int totalBytesRead = 0;
            int bytesRead;

            while ((bytesRead = stream.Read(readBuffer, totalBytesRead, readBuffer.Length - totalBytesRead)) > 0)
            {
                totalBytesRead += bytesRead;

                if (totalBytesRead == readBuffer.Length)
                {
                    int nextByte = stream.ReadByte();
                    if (nextByte != -1)
                    {
                        byte[] temp = new byte[readBuffer.Length * 2];
                        Buffer.BlockCopy(readBuffer, 0, temp, 0, readBuffer.Length);
                        Buffer.SetByte(temp, totalBytesRead, (byte)nextByte);
                        readBuffer = temp;
                        totalBytesRead++;
                    }
                }
            }

            byte[] buffer = readBuffer;
            if (readBuffer.Length != totalBytesRead)
            {
                buffer = new byte[totalBytesRead];
                Buffer.BlockCopy(readBuffer, 0, buffer, 0, totalBytesRead);
            }
            return buffer;
        }
        finally
        {
            stream.Position = originalPosition; 
        }
    }
pho3nix
Not sure I'd agree with the Length*2 buffer expansion policy there.
Remus Rusanu
Yes its true. But i use this code in frameworks 1.1 and 2.0. Because this is so big. Your answer is good to.
pho3nix
If you want to be able to read streams of arbitrary length, pretty much all of that is needed. You could use a List<byte> and save some code..
Thorarin
A bunch of concerns are mixed together in one big method. Yes, it all has to be done, but not all in one function. There's the growable byte array and there's the stream reading. Much easier to get right if they're separated.
Daniel Earwicker
That code could be made much simpler by using a MemoryStream...
Thomas Levesque
@Thomas - indeed, see my answer - the only custom code you need to write is a stream copy function that can then be reused for copying a stream directly to any other stream.
Daniel Earwicker
Looks like a modified version of http://www.yoda.arachsys.com/csharp/readbinary.html
SwDevMan81
This code example doesn't consider streams that are not seekable. Setting stream.Position should only be done when stream.CanSeek is true.
Matt Z
A: 
Stream s;
int len = (int)s.Length;
byte[] b = new byte[len];
int pos = 0;
while((r = s.Read(b, pos, len - pos)) > 0) {
    pos += r;
}

A slightly more complicated solution is necesary is s.Length exceeds Int32.MaxValue. But if you need to read a stream that large into memory, you might want to think about a different approach to your problem.

Edit: If your stream does not support the Length property, modify using Earwicker's workaround.

public static class StreamExtensions {
    // Credit to Earwicker
    public static void CopyStream(this Stream input, Stream output) {
        byte[] b = new byte[32768];
        int r;
        while ((r = input.Read(b, 0, b.Length)) > 0) {
            output.Write(b, 0, r);
        }
    }
}

[...]

Stream s;
MemoryStream ms = new MemoryStream();
s.CopyStream(ms);
byte[] b = ms.GetBuffer();
Jason
That would be great if it said Read instead of Write!
Daniel Earwicker
He did say read. He wanted to convert the stream into byte[], which is a Read, not a Write.
John Saunders
Another problem with this (I've just remembered) is that the Read method may not return all the data in one go.
Daniel Earwicker
A: 

Quick and dirty technique:

    static byte[] StreamToByteArray(Stream inputStream)
    {
        if (!inputStream.CanRead)
        {
            throw new ArgumentException(); 
        }

        // This is optional
        if (inputStream.CanSeek)
        {
            inputStream.Seek(0, SeekOrigin.Begin);
        }

        byte[] output = new byte[inputStream.Length];
        int bytesRead = inputStream.Read(output, 0, output.Length);
        Debug.Assert(bytesRead == output.Length, "Bytes read from stream matches stream length");
        return output;
    }

Test:

    static void Main(string[] args)
    {
        byte[] data;
        string path = @"C:\Windows\System32\notepad.exe";
        using (FileStream fs = File.Open(path, FileMode.Open, FileAccess.Read))
        {
            data = StreamToByteArray(fs);
        }

        Debug.Assert(data.Length > 0);
        Debug.Assert(new FileInfo(path).Length == data.Length); 
    }

I would ask, why do you want to read a stream into a byte[], if you are wishing to copy the contents of a stream, may I suggest using MemoryStream and writing your input stream into a memory stream.

Phil Price
Not all Streams support the Length property
John Saunders
There's absolutely no guarantee that Read() returns all bytes to be read.
Stefan Schultze
+4  A: 

The handy helper function to have is:

public static void CopyStream(Stream input, Stream output)
{
    byte[] b = new byte[32768];
    int r;
    while ((r = input.Read(b, 0, b.Length)) > 0)
        output.Write(b, 0, r);
}

Then use that to copy to a MemoryStream and call GetBuffer on it:

Stream file = new FileStream("c:\\foo.txt", FileMode.Open);

MemoryStream mem = new MemoryStream();
CopyStream(file, mem);

// getting the internal buffer (no additional copying)
byte[] buffer = mem.GetBuffer();
long length = mem.Length; // the actual length of the data 
                          // (the array may be longer)

// if you need the array to be exactly as long as the data
byte[] truncated = mem.ToArray(); // makes another copy

Edit: originally I suggested using Jason's answer for a Stream that supports the Length property. But it had a flaw because it assumed that the Stream would return all its contents in a single Read, which is not necessarily true (not for a Socket, for example.) I don't know if there is an example of a Stream implementation in the BCL that does support Length but might return the data in shorter chunks than you request, but as anyone can inherit Stream this could easily be the case.

It's probably simpler for most cases to use the above general solution, but supposing you did want to read directly into an array that is bigEnough:

byte[] b = new byte[bigEnough];
int r, offset;
while ((r = input.Read(b, offset, b.Length - offset)) > 0)
    offset += r;

That is, repeatedly call Read and move the position you will be storing the data at.

Daniel Earwicker
Good point, nice workaround.
Jason
Could you edit the answer to include an example of using MemoryStream in this way?
John Saunders
@John - Have done so.
Daniel Earwicker
Would be very interested in reasons for downvotes!
Daniel Earwicker
@Earwicker: I didn't downvote because you kindly made the edit I requested. OTOH, if I hadn't asked, I would have downvoted for lack of "using" statments. I actually decided not to mention that in my request, too!
John Saunders
Why bother with memorystream when you could just use a List<byte> and AddRange()? It's doing exactly the same anyway under the hood as far as I know.
DrJokepu
@DrJokepu - because stream-to-stream copying is generally useful in other situations. You only have to write that one method, and you get stream-to-stream copying and stream-to-array copying.
Daniel Earwicker
@John Saunders - that CopyStream method definitely shouldn't have using statements in it, so that would have been an odd request. The example usage might need one on the FileStream - but it might not (depends whether the rest of the code wants to reuse the same FileStream somehow).
Daniel Earwicker
A: 

You could try writing the data to a file first then using the File.ReadAllByes function.

public byte[] StreamToFileToArray(string fileName)
{
    // Setup whatever read size you want (small here for testing)
    byte[] buffer = new byte[32];
    string dump_file = "Dump.bin";
    using (Stream input = File.Open(fileName, FileMode.Open, FileAccess.Read))
    {
        using (BinaryWriter output = new BinaryWriter(new FileStream(dump_file, FileMode.Create)))
        {
            int bytesRead = input.Read(buffer, 0, buffer.Length);
            while (bytesRead > 0)
            {
                output.Write(buffer, 0, bytesRead);
                bytesRead = input.Read(buffer, 0, buffer.Length);
            }
        }
    }
    return File.ReadAllBytes(dump_file);
}
SwDevMan81
+1  A: 

You could also try just reading in parts at a time and expanding the byte array being returned:

public byte[] StreamToByteArray(string fileName)
{
    byte[] total_stream = new byte[0];
    using (Stream input = File.Open(fileName, FileMode.Open, FileAccess.Read))
    {
        byte[] stream_array = new byte[0];
        // Setup whatever read size you want (small here for testing)
        byte[] buffer = new byte[32];// * 1024];
        int read = 0;

        while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
        {
            stream_array = new byte[total_stream.Length + read];
            total_stream.CopyTo(stream_array, 0);
            Array.Copy(buffer, 0, stream_array, total_stream.Length, read);
            total_stream = stream_array;
        }
    }
    return total_stream;
}
SwDevMan81