.NET
C#
memory stream
file compression
unzip files

How can I unzip a file to a .NET memory stream?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

In .NET, unzipping "to a MemoryStream" usually means extracting one file from a ZIP archive and copying the decompressed bytes into memory. A ZIP container can hold many entries, so the practical task is to open the archive, pick the entry you want, copy it, and reset the stream position before returning it.

Extracting a Single Entry from a ZIP File

The built-in types for this are ZipArchive and ZipArchiveEntry from System.IO.Compression. The basic workflow is file stream, archive, entry lookup, copy, rewind.

csharp
1using System;
2using System.IO;
3using System.IO.Compression;
4
5public static class ZipReader
6{
7    public static MemoryStream ExtractEntryToMemory(string zipPath, string entryName)
8    {
9        using var fileStream = File.OpenRead(zipPath);
10        using var archive = new ZipArchive(fileStream, ZipArchiveMode.Read);
11
12        var entry = archive.GetEntry(entryName);
13        if (entry == null)
14            throw new FileNotFoundException($"ZIP entry '{entryName}' was not found.");
15
16        using var entryStream = entry.Open();
17        var output = new MemoryStream();
18        entryStream.CopyTo(output);
19        output.Position = 0;
20
21        return output;
22    }
23}

The key line is output.Position = 0. After CopyTo, the stream cursor sits at the end. If you forget to rewind it, any caller that reads immediately will get zero bytes and the unzip code will look broken.

Reading the Entry as Text

Many real tasks involve JSON, CSV, or XML stored inside the ZIP. In that case, you can still extract to MemoryStream, then read from it with a StreamReader.

csharp
1using var stream = ZipReader.ExtractEntryToMemory("reports.zip", "daily/report.json");
2using var reader = new StreamReader(stream);
3
4string json = reader.ReadToEnd();
5Console.WriteLine(json);

Returning a MemoryStream is useful when the next layer expects a stream API, not just a string or byte array. That makes the helper reusable across binary and text scenarios.

Working from ZIP Bytes Already in Memory

Sometimes the archive itself comes from a network request, blob store, or database. In that case, open the ZIP from a MemoryStream created from the archive bytes.

csharp
1using System.IO;
2using System.IO.Compression;
3
4public static MemoryStream ExtractEntry(byte[] zipBytes, string entryName)
5{
6    using var zipStream = new MemoryStream(zipBytes);
7    using var archive = new ZipArchive(zipStream, ZipArchiveMode.Read);
8
9    var entry = archive.GetEntry(entryName)
10        ?? throw new FileNotFoundException($"ZIP entry '{entryName}' was not found.");
11
12    using var entryStream = entry.Open();
13    var result = new MemoryStream();
14    entryStream.CopyTo(result);
15    result.Position = 0;
16
17    return result;
18}

This pattern is common in ASP.NET applications that accept uploaded ZIP files and need to inspect an internal document without writing temporary files to disk.

Choosing the Right Entry

Archives often contain directories and nested paths. GetEntry expects the internal path exactly as stored in the ZIP. If you are unsure what is inside, inspect archive.Entries first.

csharp
1using var archive = new ZipArchive(File.OpenRead("reports.zip"), ZipArchiveMode.Read);
2
3foreach (var entry in archive.Entries)
4{
5    Console.WriteLine($"{entry.FullName} | {entry.Length} bytes");
6}

That small diagnostic step is often faster than guessing entry names and debugging repeated null results.

Multiple Entries and Memory Use

If you need every file in the archive, you can loop through archive.Entries and copy each entry into its own stream or byte array. The design question is whether you actually should. A ZIP can decompress to much more data than its compressed size suggests.

csharp
1var extracted = new List<(string Name, byte[] Data)>();
2
3using var archive = new ZipArchive(File.OpenRead("bundle.zip"), ZipArchiveMode.Read);
4
5foreach (var entry in archive.Entries)
6{
7    if (string.IsNullOrEmpty(entry.Name))
8        continue;
9
10    using var entryStream = entry.Open();
11    using var ms = new MemoryStream();
12    entryStream.CopyTo(ms);
13    extracted.Add((entry.FullName, ms.ToArray()));
14}

For large archives, streaming one entry at a time is safer than keeping everything resident in memory.

Common Pitfalls

The most common issue is forgetting to rewind the returned MemoryStream. Another is assuming a ZIP contains exactly one file, when it may contain many entries and directories. Developers also confuse the compressed container stream with the decompressed entry stream, which leads to code that reads the wrong bytes. Finally, extracting everything into memory can become expensive very quickly if the archive expands to tens or hundreds of megabytes.

Summary

  • Use ZipArchive to open the ZIP and GetEntry to select a specific file.
  • Copy the entry stream into a MemoryStream, then set Position back to 0.
  • The same approach works whether the ZIP comes from disk or an in-memory byte array.
  • Inspect archive.Entries when you are unsure about internal paths.
  • Be careful with large or multi-file archives, because decompressed size can be much larger than compressed size.

Course illustration
Course illustration

All Rights Reserved.