Zip files are the most ubiquitous compression format in computing history, with it first being invented in 1986 by Phillip Katz. Since then, the Zip file has found its way into every operating system as the de facto compression method. It’s only right that .NET developers become familiar with the format and have the necessary tools to work with these files programmatically.

In this post, we’ll look at the ZipFile utility class, how to compress files and directories, along with decompressing zip files.

Using ZipFile System.IO.Compression

Microsoft introduced the ZipFile class in .NET Framework 4.5, and developers can use the utility class on .NET 5, .NET Core 1.0+, UWP, and Xamarin.Forms targets. The ZipFile static class is dependent on access to the file system; as such, any application looking to use this class also needs to reference the System.IO.Compression.FileSystem assembly.

Create A Zip File Examples

The ZipFile class has a CreateFromDirectory method that takes a target directory and a destination path for the resulting zip file.

using System;
using System.IO;
using System.IO.Compression;

// using a target directory
// ZipFile will create
// the zip file on disk
ZipFile.CreateFromDirectory(
    @"./",
    @"./archive.zip"
);

Using the CreateFromDirectory method, we can set several variables, including the compression level to Fastest, Optimal, and NoCompression. The default compression seems to be Optimal from looking through the code and is internally set on the ZipEntry class when we provide no value.

using System;
using System.IO;
using System.IO.Compression;

ZipFile.CreateFromDirectory(
    @"./", 
     "archive.zip", 
    compressionLevel: CompressionLevel.Fastest, 
    includeBaseDirectory: false, 
    entryNameEncoding: Encoding.UTF8);

For developers who want more control compressing a directory, they can loop through a directory’s contents and add files individually. The following technique is useful when we only want to add particular files, i.e., images or video files. It’s important to call Dispose on the ZipArchive to ensure the creation of a valid zip file. In the example below, we are declaring a scoped variable with the using keyword.

using System;
using System.IO;
using System.IO.Compression;

var images = Directory.GetFiles(@"./", "*.png");

if (File.Exists("archive.zip"))
    File.Delete("archive.zip");

using var archive = 
    ZipFile
    .Open(@"archive.zip", ZipArchiveMode.Create);

foreach (var image in images)
{
    var entry =
        archive.CreateEntryFromFile(
            image,
            Path.GetFileName(image),
            CompressionLevel.Optimal
        );
    
    Console.WriteLine($"{entry.FullName} was compressed.");
}

If we need to archive an entity that only exists in memory, we can use Stream objects. We can use the CopyToAsync method found on most streams to copy directly into our ZipEntry instance.

using System;
using System.IO;
using System.IO.Compression;

var images = Directory.GetFiles(@"./", "*.png");
if (File.Exists("archive.zip"))
    File.Delete("archive.zip");
using var archive =
    ZipFile
        .Open(@"archive.zip", ZipArchiveMode.Create);

// we're getting a file stream
// here but it could be any stream
var file = File.OpenRead(images[0]);
var entry =
    archive.CreateEntry(
        "test.png",
        CompressionLevel.Optimal
    );
await file.CopyToAsync(entry.Open());
Console.WriteLine($"{entry.FullName} was compressed.");

These are a few ways to create zip files in a C# application, but what about retrieving entries from a zip file?

Decompressing a Zip File Examples

The most straightforward approach is to use the ExtractToDirectory method found on the ZipFile class. This method will write entries to a directory on disk, which can then we can then access using our file system.

using System;
using System.IO;
using System.IO.Compression;

ZipFile.ExtractToDirectory(
    @"./archive.zip",
    @"./destination_directory",
    overwriteFiles: true
);

If we need to perform post-processing for zip file entries, we can use the OpenFile method on ZipFile to create a ZipArchive instance.

using System;
using System.IO;
using System.IO.Compression;

var archive = ZipFile.OpenRead(@"./archive.zip");

foreach (var entry in archive.Entries)
{
    Console.WriteLine(entry.Name);
    var stream = entry.Open();
    
    // read stream
    // push to the cloud?
    // do whatever you like!
}

As we can see, the process of working with an existing zip file and its contents is straightforward, and the ability to work with Stream instances gives us the ability to push resources from zip files to any other storage mechanism.

Conclusion

Zip files are an essential file format for a developer to understand. While not the most efficient compression format, it is ubiquitous and understood by all technology stacks. It allows us to pass logically grouped files in a single stream of data, which can be useful in many user scenarios. Luckily for .NET developers, the ZipFile utility class provides more than enough functionality to deal with this compression approach, along with a multitude of ways we can work with this file format.

If you found this post helpful, please share it with your friends and co-workers. As always, thanks for reading.

References