ChatGPT解决这个技术问题 Extra ChatGPT

How do I convert a Stream into a byte[] in C#? [duplicate]

This question already has answers here: Creating a byte array from a stream (18 answers) Closed 5 years ago.

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

Not really the answer to the question but if your Stream comes from a file you can use File.ReadAllBytes(path) to get the bytes array in one line.
@JesusJimenez you save me a lot of time of implementation, I know than has pass a lot of time from your answer but thanks a lot

J
James Dingle

The shortest solution I know:

using(var memoryStream = new MemoryStream())
{
  sourceStream.CopyTo(memoryStream);
  return memoryStream.ToArray();
}

Side note: CopyTo is only available with .NET Framework 4.
Yes it does. You could use MemoryStream.GetBuffer() to avoid the extra copy but beware that the size of the array returned is not the size of the data.
If the length of the source stream is known upfront, it is better to specify the capacity of the MemoryStream with this length; the internal buffer will have the proper size. If the length is not known, then writing to MemoryStream implies potential multiple copies of the internal buffer as the data is written and the buffer extended, and in that case the extra copy of ToArray is not necessarily the main problem.
MemoryStream is IDisposable - shouldn't it be wrapped in a using?
Corrected. There is no real need in the case of MemoryStream (dig in the source code, it does nothing), but this could change.
M
Mark A. Donohoe

Call next function like

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

Function:

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

    if(stream.CanSeek)
    {
         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
    {
        if(stream.CanSeek)
        {
             stream.Position = originalPosition; 
        }
    }
}

Not sure I'd agree with the Length*2 buffer expansion policy there.
If you want to be able to read streams of arbitrary length, pretty much all of that is needed. You could use a List and save some code..
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.
That code could be made much simpler by using a MemoryStream...
Looks like a modified version of yoda.arachsys.com/csharp/readbinary.html
J
JCH2k

I use this extension class:

public static class StreamExtensions
{
    public static byte[] ReadAllBytes(this Stream instream)
    {
        if (instream is MemoryStream)
            return ((MemoryStream) instream).ToArray();

        using (var memoryStream = new MemoryStream())
        {
            instream.CopyTo(memoryStream);
            return memoryStream.ToArray();
        }
    }
}

Just copy the class to your solution and you can use it on every stream:

byte[] bytes = myStream.ReadAllBytes()

Works great for all my streams and saves a lot of code! Of course you can modify this method to use some of the other approaches here to improve performance if needed, but I like to keep it simple.


if (instream is MemoryStream) return ((MemoryStream) instream).ToArray(); Changed to: var ms = instream as MemoryStream; if (ms != null) return ms.ToArray();
Even better with C# 7: if (instream is MemoryStream memoryStream) return memoryStream.ToArray();
D
Daniel Earwicker

In .NET Framework 4 and later, the Stream class has a built-in CopyTo method that you can use.

For earlier versions of the framework, 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 one of the above methods to copy to a MemoryStream and call GetBuffer on it:

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

var mem = new MemoryStream();

// If using .NET 4 or later:
file.CopyTo(mem);

// Otherwise:
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.


Why bother with memorystream when you could just use a List and AddRange()? It's doing exactly the same anyway under the hood as far as I know.
@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.
@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).
As I just got an upvote on this out of the blue, I noticed it is overdue for an update as Stream now has a CopyTo method that does exactly what the CopyStream did.
c
carlodurso
Byte[] Content = new BinaryReader(file.InputStream).ReadBytes(file.ContentLength);

I could be wrong, but this seems to be more efficient than the MemoryStream way that creates two copies in memory.
Depends on the scenario, your example is very specific to the file stream which you can determine the content length. What about if the input is a stream? as Readbytes only accepts the int32
nice, but BinaryReader is disposable, so this should use using.
S
Sharon AS
    byte[] buf;  // byte array
    Stream stream=Page.Request.InputStream;  //initialise new stream
    buf = new byte[stream.Length];  //declare arraysize
    stream.Read(buf, 0, buf.Length); // read from stream to byte array

If I recall correctly "Read" doesn't always read the entire available amount from the stream - eg request N bytes, return M bytes with M < N. Hence the various methods to build a buffer and read a number of times. msdn.microsoft.com/en-us/library/…
j
johnnyRose

Ok, maybe I'm missing something here, but this is the way I do it:

public static Byte[] ToByteArray(this Stream stream) {
    Int32 length = stream.Length > Int32.MaxValue ? Int32.MaxValue : Convert.ToInt32(stream.Length);
    Byte[] buffer = new Byte[length];
    stream.Read(buffer, 0, length);
    return buffer;
}

For this method and @user734862's method I got the following error: 'This stream does not support seek operations' a System.NotSupportedException. I think this may be down to the fact that I'm reading a file from a http location and then sending it back. It may well be different for when you are working with a file on your system.
Stream.Read method can read less bytes than you request. You should check return value of Read method.
The stream returned from, e.g., Microsoft.SharePoint.Client.File.OpenBinaryDirect very often returns only 500 or so bytes at a time, no matter how big your buffer is. You should never ignore the return value of Stream.Read.
But look closely to the code. The buffer is created based on stream.Length information. It will never be too big for the stream. It may be too small (Int32.MaxValue is the maximum size for it), but this is very unlikely to happen in most cases.
Ths problem is that Stream.Read doesn't always read length bytes - it can decide to read less (and returns the actual number of bytes read). You have to call this in a loop to make it work universally!
S
Savas Adar

if you post a file from mobile device or other

    byte[] fileData = null;
    using (var binaryReader = new BinaryReader(Request.Files[0].InputStream))
    {
        fileData = binaryReader.ReadBytes(Request.Files[0].ContentLength);
    }

Should mention that you can actually use this on any FileStream. In WPF, you can't use Request.Files[0].InputStream, but you can do using (FileStream fs = new File.OpenRead(fileName)) { var binaryReader = new BinaryReader(fs); fileData = binaryReader.ReadBytes((int)fs.Length); }. Thanks for the tip!
C
Community
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();

That would be great if it said Read instead of Write!
He did say read. He wanted to convert the stream into byte[], which is a Read, not a Write.
Another problem with this (I've just remembered) is that the Read method may not return all the data in one go.
P
Phil Price

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.


Not all Streams support the Length property
There's absolutely no guarantee that Read() returns all bytes to be read.
S
SwDevMan81

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

A
ArtK

"bigEnough" array is a bit of a stretch. Sure, buffer needs to be "big ebough" but proper design of an application should include transactions and delimiters. In this configuration each transaction would have a preset length thus your array would anticipate certain number of bytes and insert it into correctly sized buffer. Delimiters would ensure transaction integrity and would be supplied within each transaction. To make your application even better, you could use 2 channels (2 sockets). One would communicate fixed length control message transactions that would include information about size and sequence number of data transaction to be transferred using data channel. Receiver would acknowledge buffer creation and only then data would be sent. If you have no control over stream sender than you need multidimensional array as a buffer. Component arrays would be small enough to be manageable and big enough to be practical based on your estimate of expected data. Process logic would seek known start delimiters and then ending delimiter in subsequent element arrays. Once ending delimiter is found, new buffer would be created to store relevant data between delimiters and initial buffer would have to be restructured to allow data disposal.

As far as a code to convert stream into byte array is one below.

Stream s = yourStream;
int streamEnd = Convert.ToInt32(s.Length);
byte[] buffer = new byte[streamEnd];
s.Read(buffer, 0, streamEnd);

same as every other answer: never do stream.Read() without checking the return value of how many it has actually read...