שתף באמצעות


VB.NET Compress & Extract Zip with Progress Bar

Question

Wednesday, January 22, 2014 5:37 AM

Hi,

I have files in a folder "test" and i want to make a zip of the contents(not the folder) and have that zip in the directory of the folder.

Sor far, all is well:

        Dim curentDate As String = System.DateTime.Now.ToShortDateString
        Dim gamepath As String = TextBox1.Text
        Dim zipPath As String = gamepath + "backup" + curentDate + ".zip"
        Dim extractPath As String = "gamepath"

'zip
        ZipFile.CreateFromDirectory(gamepath, zipPath)
'extract
        ZipFile.ExtractToDirectory(zipPath, extractPath)

But, how do I add a progress bar and a percentage label? I don't mind if the percentage has decimals as long as it's only to 2 decimal points. 

Thanks so much,

David Wu

All replies (12)

Wednesday, January 22, 2014 8:10 PM ✅Answered

You'll have to extract each file individually so that you have a chance to update the progress bar.   Keep in mind that the process is likely to occur much faster than the progressbar can actually update unless the archive is very large.

Here's a brief example of one way to do it:

'Add references to:
'  System.IO.Compression
'  System.IO.Compression.FileSystem
Imports System.IO
Imports System.IO.Compression

Public Class Form1
    Friend ProgressBar1 As New ProgressBar
    Friend WithEvents Button1 As New Button With {.Top = ProgressBar1.Height + 4}

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Controls.Add(ProgressBar1)
        Controls.Add(Button1)
    End Sub

    Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Button1.Enabled = False
        Await ExtractAllAsync("c:\test\test.zip", "c:\test\test extract", ProgressBar1)
        Button1.Enabled = True
    End Sub

    Private Async Function ExtractAllAsync(zipFilePath As String, extractPath As String, progress As ProgressBar) As Task
        Try
            Dim extractCount, skipCount, errorCount As Integer
            Using archive As ZipArchive = ZipFile.OpenRead(zipFilePath)
                progress.Minimum = 0
                progress.Maximum = archive.Entries.Count
                Await Task.Run(Sub()
                                   Dim count As Integer = archive.Entries.Count
                                   For i As Integer = 0 To count - 1
                                       Try
                                           Dim entry As ZipArchiveEntry = archive.Entries(i)
                                           entry.ExtractToFile(Path.Combine(extractPath, entry.FullName))
                                           extractCount += 1
                                       Catch ioex As IOException
                                           If ioex.Message.EndsWith("already exists.") Then
                                               skipCount += 1
                                           Else
                                               errorCount += 1
                                           End If
                                       Catch ex As Exception
                                           errorCount += 1
                                       End Try
                                       Invoke(Sub() progress.Value += 1)
                                       'System.Threading.Thread.Sleep(100) 'might want delay to see progress animation better
                                   Next
                                   'System.Threading.Thread.Sleep(100)
                               End Sub)
            End Using
            MessageBox.Show(String.Format("Extracted {0} files, skipping {1} because they exist, with {2} errors.", extractCount, skipCount, errorCount), "Extraction Complete", MessageBoxButtons.OK, MessageBoxIcon.Information)
            progress.Value = 0
        Catch ex As Exception
            MessageBox.Show(ex.Message, "Error Opening Zip File", MessageBoxButtons.OK, MessageBoxIcon.Error)
        End Try
    End Function
End Class

Reed Kimble - "When you do things right, people won't be sure you've done anything at all"


Thursday, January 23, 2014 8:07 PM ✅Answered

Well I changed it by using some timers.

Although it seems to me that backgroundworkers are passing variables that were created in another thread information without crossthread issues. So I'm not sure why a background worker can pass a variable information but not a control information without a crossthread issue.

This code is pretty much in line with what Noodles LV proposed.

I'll post some images a bit later.

Option Strict On

' NOTE: Only works with .Net 4.5 or above. Compile to .Net 4.5 or above.
' Also crashes if you attempt to zip a shortcut.

Imports System.IO
Imports System.IO.Compression ' Add reference to this in Assemblies as well as a reference to System.IO.Compression.FileSystem I think.

Public Class Form1

    ' How to: Compress and Extract Files http://msdn.microsoft.com/en-us/library/ms404280(v=vs.110).aspx

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Me.CenterToScreen()
        Me.BackColor = Color.PapayaWhip
        Me.Text = "Zip and Unzip files using .Net 4.5 System.IO.Compression"
        Button1.BackColor = Color.Lime
        Button2.BackColor = Color.Lime
        Button3.BackColor = Color.Lime
        Button4.BackColor = Color.LightGray
        Button4.Enabled = False
        ProgressBar1.Minimum = 0
        ProgressBar1.Maximum = 100
        Label1.Text = "Waiting"
        Label2.Text = "Waiting"
        ListView1.MultiSelect = False
        ListView1.GridLines = True
        ListView1.LabelEdit = True
        ListView1.UseCompatibleStateImageBehavior = False
        ListView1.View = View.Details
        ListView1.Sorting = SortOrder.Ascending
        ListView1.Columns.Add("Files to be Zipped", 434, HorizontalAlignment.Left)
        Timer1.Interval = 1
        Timer2.Interval = 1
    End Sub

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Button1.Enabled = False
        Button2.Enabled = False
        Button3.Enabled = False
        Button1.BackColor = Color.OrangeRed
        Button2.BackColor = Color.LightGray
        Button3.BackColor = Color.LightGray
        Dim SFD As New SaveFileDialog
        SFD.Title = "Create Zip file. Type in name (i.e. ZipFile), do not add .Zip extension."
        SFD.InitialDirectory = "C:\Users\John\Desktop"
        SFD.DefaultExt = ""
        SFD.Filter = "Zip Files (*.Zip)|*.Zip"
        If SFD.ShowDialog = Windows.Forms.DialogResult.OK Then
            My.Computer.FileSystem.CreateDirectory(SFD.FileName.ToString.Replace(".Zip", ""))
            ZipFile.CreateFromDirectory(SFD.FileName.ToString.Replace(".Zip", ""), SFD.FileName)
            If My.Computer.FileSystem.DirectoryExists(SFD.FileName.ToString.Replace(".Zip", "")) Then
                My.Computer.FileSystem.DeleteDirectory(SFD.FileName.ToString.Replace(".Zip", ""), FileIO.DeleteDirectoryOption.DeleteAllContents)
            End If
        End If
        Button1.Enabled = True
        Button2.Enabled = True
        Button3.Enabled = True
        Button1.BackColor = Color.Lime
        Button2.BackColor = Color.Lime
        Button3.BackColor = Color.Lime
    End Sub


    Dim FilesToZipSize As Integer = 0
    Dim FileInfo() As String
    Dim ZipFiletoUse As String = ""

    Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
        Button1.Enabled = False
        Button2.Enabled = False
        Button3.Enabled = False
        Button1.BackColor = Color.LightGray
        Button2.BackColor = Color.OrangeRed
        Button3.BackColor = Color.LightGray

        Dim OFD1 As New OpenFileDialog
        OFD1.Title = "Select Zip file to use"
        OFD1.InitialDirectory = "C:\Users\John\Desktop"
        OFD1.Filter = "Zip Files (*.Zip)|*.Zip"
        OFD1.Multiselect = False

        Dim OFD2 As New OpenFileDialog
        OFD2.Title = "Select files to Zip"
        OFD2.Multiselect = True
        OFD2.InitialDirectory = "C:\Users\John\Desktop"
        OFD2.Filter = "All Files|*.*"

        If OFD1.ShowDialog = Windows.Forms.DialogResult.OK Then
            ZipFileToUse = OFD1.FileName
            If OFD2.ShowDialog = Windows.Forms.DialogResult.OK Then
                For Each Item In OFD2.FileNames
                    FilesToZipSize += CInt(FileLen(Item))
                    ListView1.Items.Add(Item)
                Next
                FileInfo = OFD2.FileNames
                BGW1Active = True
                Timer1.Start()
                ProgressBar1.Value = 0
                BackgroundWorker1.RunWorkerAsync()
            End If
        End If
    End Sub

    Dim ZipFolderToUnzipFilesFrom As String = ""

    Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
        Button1.Enabled = False
        Button2.Enabled = False
        Button3.Enabled = False
        Button1.BackColor = Color.LightGray
        Button2.BackColor = Color.LightGray
        Button3.BackColor = Color.OrangeRed
        CheckedListBox1.Items.Clear()
        Dim OFD As New OpenFileDialog
        OFD.Title = "Select Zip folder to use"
        OFD.InitialDirectory = "C:\Users\John\Desktop"
        OFD.Filter = "Zip Files (*.Zip)|*.Zip"
        OFD.Multiselect = False
        If OFD.ShowDialog = Windows.Forms.DialogResult.OK Then
            ZipFolderToUnzipFilesFrom = OFD.FileName
            Using archive As ZipArchive = ZipFile.OpenRead(OFD.FileName)
                For Each entry As ZipArchiveEntry In archive.Entries
                    CheckedListBox1.Items.Add(entry.ToString)
                Next
            End Using
            If CheckedListBox1.Items.Count = 0 Then
                Button1.Enabled = True
                Button2.Enabled = True
                Button3.Enabled = True
                Button1.BackColor = Color.Lime
                Button2.BackColor = Color.Lime
                Button3.BackColor = Color.Lime
            End If
        End If

    End Sub

    Private Sub CheckedListBox1_SelectedIndexChanged(sender As Object, e As EventArgs) Handles CheckedListBox1.SelectedIndexChanged
        If CheckedListBox1.CheckedItems.Count > 0 Then
            Button4.Enabled = True
            Button4.BackColor = Color.Lime
        End If
    End Sub

    Dim FolderToUnzipFilesTo As String = ""
    Dim FilesToUnzipSize As Integer = 0

    Private Sub Button4_Click(sender As Object, e As EventArgs) Handles Button4.Click
        Button3.BackColor = Color.LightGray
        If CheckedListBox1.CheckedItems.Count = 0 Then
            Button1.Enabled = True
            Button2.Enabled = True
            Button3.Enabled = True
            Button1.BackColor = Color.Lime
            Button2.BackColor = Color.Lime
        Else
            Button4.Enabled = False
            Button4.BackColor = Color.OrangeRed
            Dim FBD As New FolderBrowserDialog
            FBD.Description = "Select folder or create folder to unzip file(s) to"
            FBD.RootFolder = Environment.SpecialFolder.Desktop
            If FBD.ShowDialog = Windows.Forms.DialogResult.OK Then
                FolderToUnzipFilesTo = FBD.SelectedPath
                Using archive As ZipArchive = ZipFile.OpenRead(ZipFolderToUnzipFilesFrom)
                    For Each Item In CheckedListBox1.CheckedItems
                        For Each entry As ZipArchiveEntry In archive.Entries
                            If entry.FullName.ToString = Item.ToString Then
                                FilesToUnzipSize += CInt(entry.Length)
                            End If
                        Next
                    Next
                End Using
                BGW2Active = True
                Timer2.Start()
                ProgressBar1.Value = 0
                BackgroundWorker2.RunWorkerAsync()
            Else
                Button1.Enabled = True
                Button2.Enabled = True
                Button3.Enabled = True
                Button1.BackColor = Color.Lime
                Button2.BackColor = Color.Lime
                Button3.BackColor = Color.Lime
                Button4.BackColor = Color.LightGray
            End If
        End If
    End Sub

    Dim BGW1Active As Boolean = False
    Dim BGW1Percent As Integer = 0
    Dim BGW1Item As String = ""

    Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
        For Each Item In FileInfo
            BGW1Item = Item
            Using zipToOpen As FileStream = New FileStream(ZipFiletoUse, FileMode.Open)
                Using archive As ZipArchive = New ZipArchive(zipToOpen, ZipArchiveMode.Update)
                    Dim TempSplit() As String
                    TempSplit = Item.Split("\"c)
                    Dim readmeEntry As ZipArchiveEntry = archive.CreateEntry(TempSplit(TempSplit.Count - 1))
                    Dim BlockSizeToRead As Integer = 4096
                    Dim Buffer As Byte() = New Byte(BlockSizeToRead - 1) {}
                    Using writer As Stream = readmeEntry.Open
                        Using Reader As FileStream = File.Open(Item, FileMode.Open)
                            Dim bytesRead As Integer, totalBytesRead As Integer, bytesMe = 0
                            While (InlineAssignHelper(bytesRead, Reader.Read(Buffer, 0, Buffer.Length - 1))) > 0
                                writer.Write(Buffer, 0, bytesRead)
                                totalBytesRead += bytesRead
                                BGW1Percent = CInt(totalBytesRead / FilesToZipSize * 100)
                            End While
                        End Using
                    End Using
                End Using
            End Using
        Next
        BGW1Active = False
    End Sub

    Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
        If BGW1Active = True Then
            ProgressBar1.Value = BGW1Percent
            Label1.Text = "File being zipped is " & BGW1Item
            Label2.Text = "Percent zipped is " & BGW1Percent.ToString & ChrW(37)
        Else
            Timer1.Stop()
            Button1.Enabled = True
            Button2.Enabled = True
            Button3.Enabled = True
            Button1.BackColor = Color.Lime
            Button2.BackColor = Color.Lime
            Button3.BackColor = Color.Lime
            ListView1.Items.Clear()
            Label1.Text = "Waiting"
            Label2.Text = "Waiting"
            ProgressBar1.Value = 0
            FilesToZipSize = 0
            BGW1Percent = 0
            BGW1Item = ""
        End If

    End Sub

    Dim BGW2Active As Boolean = False
    Dim BGW2Percent As Integer = 0
    Dim BGW2Item As String = ""

    Private Sub BackgroundWorker2_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker2.DoWork
        Using archive As ZipArchive = ZipFile.OpenRead(ZipFolderToUnzipFilesFrom)
            For Each Item In CheckedListBox1.CheckedItems
                For Each entry As ZipArchiveEntry In archive.Entries
                    If entry.FullName.ToString = Item.ToString Then
                        BGW2Item = Item.ToString
                        Using Reader As Stream = entry.Open
                            Using Writer As FileStream = File.Open(Path.Combine(FolderToUnzipFilesTo, Item.ToString), FileMode.OpenOrCreate)
                                Dim bytesWritten As Integer, totalBytesWritten As Integer, bytesMe = 0
                                Dim BlockSizeToWrite As Integer = 4096
                                Dim Buffer As Byte() = New Byte(BlockSizeToWrite - 1) {}
                                While (InlineAssignHelper(bytesWritten, Reader.Read(Buffer, 0, Buffer.Length - 1))) > 0
                                    Writer.Write(Buffer, 0, bytesWritten)
                                    totalBytesWritten += bytesWritten
                                    BGW2Percent = CInt(totalBytesWritten / FilesToUnzipSize * 100)
                                End While
                            End Using
                        End Using
                    End If
                Next
            Next
        End Using
        BGW2Active = False
    End Sub

    Private Sub Timer2_Tick(sender As Object, e As EventArgs) Handles Timer2.Tick
        If BGW2Active = True Then
            ProgressBar1.Value = BGW2Percent
            Label1.Text = "File being unzipped is " & BGW2Item
            Label2.Text = "Percent unzipped is " & BGW2Percent.ToString & ChrW(37)
        Else
            Timer2.Stop()
            Button1.Enabled = True
            Button2.Enabled = True
            Button3.Enabled = True
            Button1.BackColor = Color.Lime
            Button2.BackColor = Color.Lime
            Button3.BackColor = Color.Lime
            Button4.BackColor = Color.LightGray
            CheckedListBox1.Items.Clear()
            Label1.Text = "Waiting"
            Label2.Text = "Waiting"
            ProgressBar1.Value = 0
            FilesToUnzipSize = 0
            BGW2Percent = 0
            BGW2Item = ""
        End If
    End Sub

    Private Function InlineAssignHelper(Of T)(ByRef target As T, value As T) As T
        target = value
        Return value
    End Function

End Class

Please BEWARE that I have NO EXPERIENCE and NO EXPERTISE and probably onset of DEMENTIA which may affect my answers! Also, I've been told by an expert, that when you post an image it clutters up the thread and mysteriously, over time, the link to the image will somehow become "unstable" or something to that effect. :) I can only surmise that is due to Global Warming of the threads.


Wednesday, January 22, 2014 7:19 AM | 1 vote

Hello,

   for extraction and compression not provides progress method, If you want progress bar updating,  Gets the total number of bytes that need to be compressed,, compress the files by whatever chunk size that you choose and then report progress at whatever level of detail that you wish.

 if the reply help you mark it as your answer.
 Free Managed .NET Word Component(Create, Modify, Convert & Print)


Wednesday, January 22, 2014 4:45 PM | 1 vote

I suppose you are using the ZipFile Class which is only available in .Net 4.5 (and later above .Net 4.5 I would guess).

I'm fairly certain that can be done but it will take a while to try it.

How to: Compress and Extract Files

Please BEWARE that I have NO EXPERIENCE and NO EXPERTISE and probably onset of DEMENTIA which may affect my answers! Also, I've been told by an expert, that when you post an image it clutters up the thread and mysteriously, over time, the link to the image will somehow become "unstable" or something to that effect. :) I can only surmise that is due to Global Warming of the threads.


Wednesday, January 22, 2014 7:34 PM

You could use the third-party DotNetZip Library. I haven't used it, but here is a thread asking about using a progress bar: Handle progress-bar.

HTH,

Andrew


Thursday, January 23, 2014 5:43 PM

@Mr.Monkeyboy

You should not post an example if you can't make it work without turning off cross-thread checking.  That property exists solely for the VB6 upgrade wizard and should never be set on purpose.

Reed Kimble - "When you do things right, people won't be sure you've done anything at all"


Thursday, January 23, 2014 5:53 PM

@Mr.Monkeyboy

You should not post an example if you can't make it work without turning off cross-thread checking.  That property exists solely for the VB6 upgrade wizard and should never be set on purpose.

Reed Kimble - "When you do things right, people won't be sure you've done anything at all"

O.K.

Please BEWARE that I have NO EXPERIENCE and NO EXPERTISE and probably onset of DEMENTIA which may affect my answers! Also, I've been told by an expert, that when you post an image it clutters up the thread and mysteriously, over time, the link to the image will somehow become "unstable" or something to that effect. :) I can only surmise that is due to Global Warming of the threads.


Thursday, January 23, 2014 6:02 PM

@Mr.Monkeyboy

You should not post an example if you can't make it work without turning off cross-thread checking.  That property exists solely for the VB6 upgrade wizard and should never be set on purpose.

Reed Kimble - "When you do things right, people won't be sure you've done anything at all"

O.K.

Please BEWARE that I have NO EXPERIENCE and NO EXPERTISE and probably onset of DEMENTIA which may affect my answers! Also, I've been told by an expert, that when you post an image it clutters up the thread and mysteriously, over time, the link to the image will somehow become "unstable" or something to that effect. :) I can only surmise that is due to Global Warming of the threads.

Thank you.

To correct your sample, look inside of your DoWork routine(s) and anywhere you need to access a control member, either do so through ReportProgress or create a delegate method to do that work and Invoke the delegate.  It is often easiest to use the generic ones like Me.Invoke(New Action(AddressOf mywork))

I'd be happy to help you understand the Action and Func delegates more if you'd like to start a thread on it.

Reed Kimble - "When you do things right, people won't be sure you've done anything at all"


Thursday, January 23, 2014 8:59 PM

You can pass variables between threads, so long as the object that the variable represents is not owned by a particular thread.  When a control instance is created, it is owned by the thread that created it and cannot (should not) be directly access by another thread.  If the variable is accessible from more than one thread, then you have to code carefully to avoid race conditions and deadlocks.  You also have to be careful that you don't inadvertently reduce performance instead of increasing it.

The BackgroundWorker made things easier if you put all of the secondary thread operations in DoWork and only interact with the primary thread through ReportProgress.

But even the BackgroundWorker has been replaced in favor of using Await in VS2012+.  I was a little late to adopt it because I hated 2012 lol but I'm in 2013 now and getting comfortable and getting used to Await has been at the top of my to-do list.  It really does make async and multithreaded code much cleaner to write and read/debug.

Reed Kimble - "When you do things right, people won't be sure you've done anything at all"


Thursday, January 23, 2014 9:07 PM

I saw Await in your example you posted but as I'm unfamiliar with it I did not want to try it with all the other issues I was having just to get the code I posted to work. In fact Async code is not in my scope of knowledge at this moment in time. And probably not soon either.

Please BEWARE that I have NO EXPERIENCE and NO EXPERTISE and probably onset of DEMENTIA which may affect my answers! Also, I've been told by an expert, that when you post an image it clutters up the thread and mysteriously, over time, the link to the image will somehow become "unstable" or something to that effect. :) I can only surmise that is due to Global Warming of the threads.


Friday, January 24, 2014 11:03 PM

Images of example working.

Please BEWARE that I have NO EXPERIENCE and NO EXPERTISE and probably onset of DEMENTIA which may affect my answers! Also, I've been told by an expert, that when you post an image it clutters up the thread and mysteriously, over time, the link to the image will somehow become "unstable" or something to that effect. :) I can only surmise that is due to Global Warming of the threads.


Sunday, January 26, 2014 5:31 AM

Look here:

http://dotnetzip.codeplex.com/downloads/get/258014

and here

http://archive.msdn.microsoft.com/DotNetZip/Release/ProjectReleases.aspx?ReleaseId=3097

has everything you asked about