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.