Converting Stream to Byte Array: a guide for C# developers (2025 edition)
\n\nHave you ever caught yourself thinking: "Why, C#, don't you just let me take this Stream and turn it into a byte array in one line?" If yes — you are not alone. The question "Creating a byte array from a stream" on Stack Overflow gathers thousands of views, and for good reason. It seems like a simple task, but the devil, as always, is in the details: performance, memory, asynchrony, and .NET versions.
In 2025, the .NET ecosystem offers us several elegant solutions. In this article, we will break down 5 best practices — from classics to new features. You will learn when to use MemoryStream, why BinaryReader is still relevant, and how not to crash your application when working with giant streams. Let's go!
1. The Classic: MemoryStream and CopyTo
\n\nThe most popular and intuitive way. If you have a Stream (e.g., from a file, network, or memory), you simply "dump" it into a MemoryStream, then call ToArray().
public static byte[] StreamToBytesUsingMemoryStream(Stream inputStream)\n{\n using (var memoryStream = new MemoryStream())\n {\n inputStream.CopyTo(memoryStream);\n return memoryStream.ToArray();\n }\n}\n\nPros:
\n- \n
- Simplicity and readability. \n
- Works in all .NET versions (Framework, Core, 5+). \n
- Supports
CopyToAsyncfor asynchronous work. \n
Cons:
\n- \n
- If the stream is huge (e.g., 1 GB), you hold data in memory twice: first in
MemoryStream, then in the array. \n - Not the fastest option for very small streams (overhead of object creation). \n
When to use: For medium-sized streams (up to 100 MB) and when code readability matters more than micro-optimizations.
\n\n2. The Async Hero: ReadAsync and Buffer
\n\nIn modern development, asynchrony is the standard. The ReadAsync method allows reading data in chunks without blocking the thread. This is especially useful in ASP.NET Core or GUI applications.
public static async Task<byte[]> StreamToBytesAsync(Stream inputStream)\n{\n using (var memoryStream = new MemoryStream())\n {\n byte[] buffer = new byte[8192]; // 8 KB — standard size\n int bytesRead;\n while ((bytesRead = await inputStream.ReadAsync(buffer, 0, buffer.Length)) > 0)\n {\n await memoryStream.WriteAsync(buffer, 0, bytesRead);\n }\n return memoryStream.ToArray();\n }\n}\n\nPro tip: The buffer size can be changed. For network streams, 8 KB is the sweet spot. For SSD drives, you can increase it to 64 KB.
\n\nPros:
\n- \n
- Does not block the calling thread. \n
- Allows processing data "on the fly" (e.g., calculating a hash). \n
- Control over memory consumption. \n
Cons:
\n- \n
- Slightly more code. \n
- Requires understanding of the async/await pattern. \n
3. BinaryReader: an old friend is better than two new ones
\n\nMany people forget about BinaryReader, but they shouldn't. It can read the entire stream with one command — ReadBytes. But there is a nuance: it needs to know the stream length in advance.
public static byte[] StreamToBytesUsingBinaryReader(Stream inputStream)\n{\n if (inputStream.CanSeek)\n {\n inputStream.Position = 0; // Reset position if needed\n using (var reader = new BinaryReader(inputStream, Encoding.UTF8, leaveOpen: true))\n {\n return reader.ReadBytes((int)inputStream.Length);\n }\n }\n else\n {\n throw new InvalidOperationException("Stream does not support seeking");