Extract file from archive with progress (VB.NET)
Introduction
The System.IO.Compression namespace contains classes for compressing and decompressing files and streams which can also use these types to read and modify the contents of a compressed file. Microsoft provides easy to follow examples on the following page. This article will provide base code to provide progress while extracting files from a compressed file using a ProgressBar or hooks to use for displaying the file being extracted name and file size with room to modified for personal requirements. Base code is used rather than a full fledge example as this leaves room for personal business requirements.
Laying a foundation
To provides the ability to rely progress to the user interface the following delegate is required which the form can subscribe too which is explained below.
Public Delegate Sub ZipEventHandler(sender As Object, e As ZipArgs)
The following class is used by the delegate above to provide current file name, current file size, percent done from total files in the compress file and a boolean to indicate if the current file was extracted.
Namespace Classes
''' <summary>
''' For ZipEventHandler delegate args
''' </summary>
Public Class ZipArgs
Inherits EventArgs
Private _fileName As String
Private _percentDone As Integer
Private _fileLength As Long
Private _extracted As Boolean
Public Sub New(
fileName As String,
fileLength As Long,
percentDone As Integer,
extracted As Boolean)
_fileName = fileName
_fileLength = fileLength
_percentDone = percentDone
_extracted = extracted
End Sub
Public ReadOnly Property FileName As String
Get
Return _fileName
End Get
End Property
Public ReadOnly Property FileLength() As Long
Get
Return _fileLength
End Get
End Property
Public ReadOnly Property PercentDone As Integer
Get
Return _percentDone
End Get
End Property
Public ReadOnly Property Extracted() As Boolean
Get
Return _extracted
End Get
End Property
End Class
End Namespace
In the form which will perform file extraction from a zip file, the delegate ZipEventHandler is setup by viewing the form in the Visual Studio code editor, selecting (Form1 events) in the center combo box of the editor then selecting ZipEventHandler in the right combo box which creates the following line of code.
Public Event ZipEventHandler As ZipEventHandler
Form code
Selecting a compressed file
A standard OpenFileDialog is used to allow the selection of a file. when a selection is made the path and file name are placed into a readonly TextBox.
Selecting a folder for file extraction
A custom Folder dialog is used to prompt for a folder location to extract files too. Before building the project right click on solution explorer and select restore NuGet packages. The custom folder dialog will work the same as a standard dialog with a better view and more options.
Purging extracted files
Before getting into extracting files, during extraction if a file already exists a IOException will be thrown. In this code sample logic will bypass existing files as this is a base code sample. If overwriting is permitted a boolean may be added to the function to extract files to permit deletion or similar logic.
See code in the purge button which provide code to delete all files in the selected extract folder.
Extracting files from archive
The following function signature accepts a zip file name with path, a folder to extract files, optionally a ProgressBar
Private Async Function ExtractAllAsync(
zipFileName As String,
extractPath As String,
Optional progressBar As ProgressBar = Nothing) As Task
All operations are wrapped in a try/multiple catch to focus on IO exception and then general exceptions. Note in the above signature ExtraxtAllAsync is a function with Task as a return type but does not actually return anything as using a sub/procedure is not wise with asynchronous operations as any exception may get swallowed.
Note to use ZipArchive add a reference to System.IO.Compression.dll and System.IO.Compression.FileSystem.dll
The first operation is to open the zip file
Using zipZrchive As ZipArchive = ZipFile.OpenRead(zipFileName)
Next assertion is done to determine if a ProgressBar was passed (see code inline).
If progressBar IsNot Nothing Then
progressBar.Minimum = 0
progressBar.Maximum = zipZrchive.Entries.Count
End If
Once finished the following code handles iterating each file in the file passed into the function.
Await Task.Run(
Sub()
Dim count As Integer = zipZrchive.Entries.Count
Dim currentFileName As String = ""
Dim currentFileLength As Long = 0
'
' Iterate files in compressed file
'
For index As Integer = 0 To count - 1
Try
Dim entry As ZipArchiveEntry = zipZrchive.Entries(index)
currentFileName = entry.FullName
'
' Bypass existing file, optional logic can be used
' to overwrite an existing file
'
If Not File.Exists(Path.Combine(extractPath, entry.FullName)) Then
entry.ExtractToFile(Path.Combine(extractPath, entry.FullName))
currentFileLength = entry.Length
extractCount += 1
extracted = True
Else
extracted = False
End If
Catch ioex As IOException
'
' By removing the logic above for File.Exists an exception
' would be thrown. To get around this the file must first
' be removed
'
If ioex.Message.EndsWith("already exists.") Then
skipCount += 1
Else
errorCount += 1
End If
Catch ex As Exception
'
' Unknown error
' Should not be ignored, for a production app
' consider writing to a log file
'
errorCount += 1
End Try
progressValue += 1
If progressBar IsNot Nothing Then
Invoke(Sub() progressBar.Value += 1)
End If
Task.Delay(100).Wait() ' REMOVE FOR PRODUCTION
'
' Pass current file name without path,
' file size of current file,
' percent done and if the file was extracted
'
' Room here for customizing what is passed
' by modifying ZipArgs class.
'
RaiseEvent ZipEventHandler(
Me,
New ZipArgs(
currentFileName,
currentFileLength,
progressValue.PercentageOf(count),
extracted))
Next
End Sub)
Key points in the code above
- entry.FullName only has the file name, extractPath contains the path selected in the folder dialog (see inline code for combining path and file name).
- To prevent IOException the following code protects against this but in this code sample assertion is used to prevent this from happening.
- The following code sets the value for progress to the ProgressBar if a ProgressBar is passed.
- For demonstration purposes the following code wait has been added in the event there are only a few files so to slow down the process, remove for production.
- The RaiseEvent is pushes elements to the listener (see inline code) which in turn pushes information to a ProgressBar is passed and also to add extracted file names and file sizes to a ListBox.
Form load code
This code is where initialization is performed e.g. set default paths for extracting files.
Summary
Code has been provided to learn how to show progress while extracting files from a zip file leaving room for customization. A more advance code sample would be counterproductive in both learning and modifying for personal use which is why the code sample is basic. Although the code is basic there are many parts e.g. asynchronous code and custom events which will be new to many developers that will take time to learn. The best way to learn when code does not make sense is to set breakpoints, run code and step through the code to examine how lines of code perform.
See also
Source code
See the following GitHub repository.