(Print this page)

System.IO.Compression
Published date: Sunday, May 28, 2017
On: Moer and Éric Moreau's web site

Every so often, it looks like Microsoft adds a compression library to its framework.

I have other articles on this blog related to compression libraries offered by the Framework. For instance, I have published one in August 2008 and another one in February 2007.

Lately (meaning .Net framework 4.5!), they have added kind of an extra layer to ease the use of compression from your projects.

This month’s downloadable code

This month solution contains both VB and C# projects. The solution was created using Visual Studio 2017 but the code should work as well in older version of the.Net Framework as long as you can target the Framework 4.5 or better.

Figure 1: The demo application in action

Required reference

In order to be able to use this library, you will need to add a reference to System.IO.Compression and to System.IO.Compression.FileSystem otherwise you will have issues with the compiler complaining some members like ZipFile and ZipArchive are not existing.

Figure 2: Adding the required references

The ZipFile class

This class provides static methods for creating (CreateFormDirectory), extracting (ExtractToDirectory), and opening (Open and OpenRead) zip archives. These methods are covering the very basics operations to easily create a zip from a complete folder and extract a full zip to a folder. Quick and easy for basic needs.

Companion classes

If you also need to add entries to an existing zip file, or delete an entry from an existing zip file, you will need to rely on the companion classes ZipArchive and ZipArchiveEntry.

CreateFromDirectory

This is very helpful method when you want to take a full folder and zip it.

Private Sub btnCreateFromDirectory_Click(sender As Object, e As EventArgs) Handles btnCreateFromDirectory.Click
    If (Not Directory.Exists(mstrStartSourcePath)) Then
        MessageBox.Show("Source path does not exists!")
        Return
    End If

    If (File.Exists(mstrZipPath)) Then
        File.Delete(mstrZipPath)
    End If
    ZipFile.CreateFromDirectory(mstrStartSourcePath, mstrZipPath)
    MessageBox.Show("Operation completed")
End Sub
private void btnCreateFromDirectory_Click(object sender, EventArgs e)
{
    if (!Directory.Exists(_startSourcePath))
    {
        MessageBox.Show("Source path does not exists!");
        return;
    }
    if (File.Exists(_zipPath))
        File.Delete(_zipPath);
    ZipFile.CreateFromDirectory(_startSourcePath, _zipPath);
    MessageBox.Show("Operation completed");
}

The sad news is that if the resulting .zip already exists, you will end up with System.IO.IOException exception. You cannot update an existing zip with this method. It has to be a completely new one.

Adding a file to an existing Zip file

Sometimes you need add files to an existing zip file. This is when you can use the Open method with the Update mode.

Private Sub btnAddFile_Click(sender As Object, e As EventArgs) Handles btnAddFile.Click
    If (Not Directory.Exists(mstrStartSourceAddPath)) Then
        MessageBox.Show("Source Add path does not exists!")
        Return
    End If

    If (Not File.Exists(mstrZipPath)) Then
        MessageBox.Show("Source Zip file does not exists!")
        Return
    End If

    Dim strFileNameToAdd As String = "LoremIpsumAdd.txt"
    Dim strNewFile As String = Path.Combine(mstrStartSourceAddPath, strFileNameToAdd)
    Using archive = ZipFile.Open(mstrZipPath, ZipArchiveMode.Update)
        archive.CreateEntryFromFile(strNewFile, strFileNameToAdd)
    End Using

    MessageBox.Show("Operation completed")
End Sub
private void btnAddFile_Click(object sender, EventArgs e)
{
    if (!Directory.Exists(_startSourceAddPath))
    {
        MessageBox.Show("Source Add path does not exists!");
        return;
    }
    if (!File.Exists(_zipPath))
    {
        MessageBox.Show("Source Zip file does not exists!");
        return;
    }

    string fileNameToAdd = "LoremIpsumAdd.txt";
    string newFile = Path.Combine(_startSourceAddPath, fileNameToAdd);
    using (ZipArchive archive = ZipFile.Open(_zipPath, ZipArchiveMode.Update))
    {
        archive.CreateEntryFromFile(newFile, fileNameToAdd);
    }

    MessageBox.Show("Operation completed");
}

ExtractToDirectory This is another helpful method when you have a zip file that you want to unzip.

Private Sub btnExtractToDirectory_Click(sender As Object, e As EventArgs) Handles btnExtractToDirectory.Click
    If (Not File.Exists(mstrZipPath)) Then
        MessageBox.Show("Source Zip file does not exists!")
        Return
    End If
    If (Directory.Exists(mstrExtractPath)) Then
        Directory.Delete(mstrExtractPath, True)
    End If

    ZipFile.ExtractToDirectory(mstrZipPath, mstrExtractPath)

    MessageBox.Show("Operation completed")
End Sub
private void btnExtractToDirectory_Click(object sender, EventArgs e)
{
    if (!File.Exists(_zipPath))
    {
        MessageBox.Show("Source Zip file does not exists!");
        return;
    }
    if (Directory.Exists(_extractPath))
        Directory.Delete(_extractPath, true);
    ZipFile.ExtractToDirectory(_zipPath, _extractPath);

    MessageBox.Show("Operation completed");
}

Here again you can get a System.IO.IOException if one of the file you are trying to unzip already exist in the output folder.

Extracting some files

The previous method is very useful when you want to extract all the files from a zip. But often you will need to extract only some files base on some criteria.

This code is extracting only .txt files.

Private Sub btnExtractTxtToDirectory_Click(sender As Object, e As EventArgs) Handles btnExtractTxtToDirectory.Click
    If (Not File.Exists(mstrZipPath)) Then
        MessageBox.Show("Source Zip file does not exists!")
        Return
    End If
    If (Not Directory.Exists(mstrExtractPath)) Then
        Directory.CreateDirectory(mstrExtractPath)
    End If

    Using archive = ZipFile.OpenRead(mstrZipPath)
        For Each entry As ZipArchiveEntry In archive.Entries
            If (entry.FullName.EndsWith(".txt", StringComparison.OrdinalIgnoreCase)) Then
                entry.ExtractToFile(Path.Combine(mstrExtractPath, entry.FullName), True)
            End If
        Next
    End Using

    MessageBox.Show("Operation completed")
End Sub
private void btnExtractTxtToDirectory_Click(object sender, EventArgs e)
{
    if (!File.Exists(_zipPath))
    {
        MessageBox.Show("Source Zip file does not exists!");
        return;
    }
    if (!Directory.Exists(_extractPath))
        Directory.CreateDirectory(_extractPath);

    using (ZipArchive archive = ZipFile.OpenRead(_zipPath))
    {
        foreach (ZipArchiveEntry entry in archive.Entries)
        {
            if (entry.FullName.EndsWith(".txt", StringComparison.OrdinalIgnoreCase))
            {
                entry.ExtractToFile(Path.Combine(_extractPath, entry.FullName), true);
            }
        }
    }

    MessageBox.Show("Operation completed");
}

Strangely here, you will get a System.IO.DirectoryNotFoundException exception if your output folder does not exist. You can also get a System.IO.IOException if the file you are trying to extract already exists but you can easily fix that issue by using the overwrite argument of the ExtractToFile method.

Deleting some entries

Last but not least, this snippet of code shows how to delete all .txt file from a zip archive.

Private Sub btnDelete_Click(sender As Object, e As EventArgs) Handles btnDelete.Click
    If (Not File.Exists(mstrZipPath)) Then
        MessageBox.Show("Source Zip file does not exists!")
        Return
    End If

    Using archive = ZipFile.Open(mstrZipPath, ZipArchiveMode.Update)
        For Each entry As ZipArchiveEntry In archive.Entries.ToList()
            If (entry.FullName.EndsWith(".txt", StringComparison.OrdinalIgnoreCase)) Then
                entry.Delete()
            End If
        Next
    End Using

    MessageBox.Show("Operation completed")
End Sub
private void btnDelete_Click(object sender, EventArgs e)
{
    if (!File.Exists(_zipPath))
    {
        MessageBox.Show("Source Zip file does not exists!");
        return;
    }

    using (ZipArchive archive = ZipFile.Open(_zipPath, ZipArchiveMode.Update))
    {
        foreach (ZipArchiveEntry entry in archive.Entries.ToList())
        {
            if (entry.FullName.EndsWith(".txt", StringComparison.OrdinalIgnoreCase))
            {
                entry.Delete();
            }
        }
    }

    MessageBox.Show("Operation completed");
}

The big error you can get here is a System.InvalidOperationException saying that the collection was modified. This is easily fixed by just adding a .ToList() to you collection. This way, LINQ will fully list the collection before starting to loop through it.

No update?

No big issues here! You can simply replace an update with a delete followed by an insert.

Conclusion

I have only scratched the surface of the library here. I have covered the main methods to get you started and a couple of hints to avoid exceptions.

If you want to know more about it, you should have a look at the MSDN documentation about the System.IO.Compression namespace.

The library presented here is not as complete as other alternatives but gives you a quick way into it if your needs are simple. Alternatives includes (but are not limited to):

I hope you appreciated the topic and see you next month.


(Print this page)