System.IO.Compression
| Language | C# / .NET |
|---|---|
| License | MIT (part of .NET) |
| Repository | github.com/dotnet/runtime |
| Documentation | ZipFile, ZipArchive |
| Depends on | nothing (built into .NET; zlib-ng is bundled in the runtime) |
| Read / write | yes / yes, ZIP64, stored and DEFLATE (reads Deflate64); no encryption |
System.IO.Compression is part of .NET and .NET Framework 4.5+. It has three classes:
ZipFile: static helpers to create an archive from a directory, extract an archive to a directory, and open an archive from a path.ZipArchive: an open archive inRead,CreateorUpdatemode, with a list of entries.ZipArchiveEntry: one file, withOpen()to get a stream and properties likeFullName,Length,CompressedLengthandLastWriteTime.
It handles ZIP64 and UTF-8 names automatically. It can’t read or write password-protected archives; use SharpZipLib or SharpCompress for those. .NET 10 adds async versions (ZipFile.OpenReadAsync, ExtractToDirectoryAsync, ZipArchiveEntry.OpenAsync…).
Installation#
Nothing to install on .NET (Core) and .NET 5+. On .NET Framework, reference the System.IO.Compression and System.IO.Compression.FileSystem assemblies.
Basic usage: create and extract a ZIP#
using System.IO.Compression;
// zip a whole folder (the folder itself is not included, only its contents)
ZipFile.CreateFromDirectory("photos", "photos.zip", CompressionLevel.Optimal, includeBaseDirectory: false);
// unzip into a folder, overwriting existing files
ZipFile.ExtractToDirectory("photos.zip", "out", overwriteFiles: true);
ExtractToDirectory throws an IOException for entries whose path would land outside the target folder, so it’s safe against Zip Slip.
How to list the files in a ZIP#
using System;
using System.IO.Compression;
using ZipArchive archive = ZipFile.OpenRead("in.zip");
foreach (ZipArchiveEntry entry in archive.Entries)
{
// directory entries have an empty Name and a FullName ending in '/'
Console.WriteLine($"{entry.FullName,-40} {entry.Length,10} -> {entry.CompressedLength,10} {entry.LastWriteTime}");
}
How to read one file from a ZIP without extracting#
using System.IO;
using System.IO.Compression;
using ZipArchive archive = ZipFile.OpenRead("in.zip");
ZipArchiveEntry? entry = archive.GetEntry("docs/readme.txt"); // exact, case-sensitive name
if (entry != null)
{
using Stream s = entry.Open();
using var reader = new StreamReader(s);
string text = reader.ReadToEnd();
// or save it: entry.ExtractToFile("readme.txt", overwrite: true);
}
How to create a ZIP from files and strings#
using System.IO;
using System.IO.Compression;
using (FileStream fs = File.Create("out.zip"))
using (var archive = new ZipArchive(fs, ZipArchiveMode.Create))
{
archive.CreateEntryFromFile("report.pdf", "docs/report.pdf", CompressionLevel.SmallestSize);
ZipArchiveEntry entry = archive.CreateEntry("hello.txt");
using StreamWriter writer = new(entry.Open());
writer.WriteLine("hello from .NET");
}
Create mode writes entries one after another and works with non-seekable streams (e.g. an HTTP response). Only one entry can be open for writing at a time.
How to add or delete files in an existing ZIP#
using System.IO.Compression;
using ZipArchive archive = ZipFile.Open("existing.zip", ZipArchiveMode.Update);
archive.CreateEntryFromFile("new.txt", "new.txt");
archive.GetEntry("old.log")?.Delete();
// changes are written when the archive is disposed
Update mode loads entries into memory when you open them, so it’s slow for large archives.
Tips#
CompressionLevel.NoCompressionstores entries; use it for files that are already compressed (JPEG, MP4, other ZIPs).- For archives with non-UTF-8 names (older Windows tools), pass an encoding:
ZipFile.Open(path, ZipArchiveMode.Read, Encoding.GetEncoding(866))(needsSystem.Text.Encoding.CodePageson .NET Core). ZipArchiveEntry.ExternalAttributesholds the Unix mode in the high 16 bits for archives made on Unix.