3

Can anyone tell me if it is possible(and give an example if it is) on how to have a progress bar(and status label if possible) showing the progress of a ZIP file being extracted using "ZipFile"(Ionic.zip, http://dotnetzip.codeplex.com/)?

My WinForm does a great job at extracting a ZIP file from my chosen path to a new path no worries at all using textboxes and browse buttons and all that jazz... However the only issue is i cannot use anything on my form during this time it is as if it has frozen, but its only because its unzipping the ZIP file in the background.

The ZIP file is a big file and i would like to make it less confusing on what is going on by adding and having a progress bar showing how the unzipping is going with a accurate ETA.

Surely this is possible, i just cannot work out how to do it in C# WinForms, i have had a fairly decent look all around the net without really being able to come across a example i can find suitable for me.

Here is a rough example of what i have:

private void button1_Click(object sender, EventArgs e)
{
    var ROBOT0007 = textBox1.Text + @"\" + "ROBOT0007"; //ROBOT0007 folder
    var ROBOT_INSTALL = textBox1.Text + @"\" + "911" + @"\" + "files"; //ROBOT0007/911/files
    var ROBOT_INSTALL_SPECIAL = ROBOT_INSTALL + @"\" + "special.rar";  //ROBOT0007/911/files/special.rar

    //If the path has text...
    if (textBox1.TextLength > 0)
    {
        //if the subfolder doesn't exist then make it.
        if (!Directory.Exists(ROBOT0007))
        {
            Directory.CreateDirectory(ROBOT0007);
        }

        //if the textbox directory exists
        if (Directory.Exists(ROBOT0007))
        {
            using (ZipFile zip = ZipFile.Read(ROBOT_INSTALL_SPECIAL))
            {
                zip.ExtractAll(ROBOT0007, ExtractExistingFileAction.OverwriteSilently);

            } 
        }
    }
}

UPDATE(04/11/2014): I have removed the textbox for now gone back to simple basics, the following works with a background worker, however the cancel button has no effect on RAR files... any advice?

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Threading;
using System.Text;
using System.Windows.Forms;
using Ionic.Zip;
using System.IO;

namespace BackgroundWorkerSample
{
    // The BackgroundWorker will be used to perform a long running action
    // on a background thread.  This allows the UI to be free for painting
    // as well as other actions the user may want to perform.  The background
    // thread will use the ReportProgress event to update the ProgressBar
    // on the UI thread.
    public partial class Form1 : Form
    {
        /// <summary>
        /// The backgroundworker object on which the time consuming operation 
        /// shall be executed
        /// </summary>
        BackgroundWorker backgroundWorker1;

        public Form1()
        {
            InitializeComponent();
            backgroundWorker1 = new BackgroundWorker();

            // Create a background worker thread that ReportsProgress &
            // SupportsCancellation
            // Hook up the appropriate events.
            backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
            backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler
                    (backgroundWorker1_ProgressChanged);
            backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler
                    (backgroundWorker1_RunWorkerCompleted);
            backgroundWorker1.WorkerReportsProgress = true;
            backgroundWorker1.WorkerSupportsCancellation = true;
        }

        /// <summary>
        /// On completed do the appropriate task
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            // The background process is complete. We need to inspect
            // our response to see if an error occurred, a cancel was
            // requested or if we completed successfully.  
            if (e.Cancelled)
            {
                lblStatus.Text = "Task Cancelled.";
            }

            // Check to see if an error occurred in the background process.

            else if (e.Error != null)
            {
                lblStatus.Text = "Error while performing background operation.";
            }
            else
            {
                // Everything completed normally.
                lblStatus.Text = "Task Completed...";
            }

            //Change the status of the buttons on the UI accordingly
            btnStart.Enabled = true;
            btnCancel.Enabled = false;
        }

        /// <summary>
        /// Notification is performed here to the progress bar
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {

            // This function fires on the UI thread so it's safe to edit

            // the UI control directly, no funny business with Control.Invoke :)

            // Update the progressBar with the integer supplied to us from the

            // ReportProgress() function.  

            progressBar1.Value = e.ProgressPercentage;
            lblStatus.Text = "Processing......" + progressBar1.Value.ToString() + "%";
        }

        /// <summary>
        /// Time consuming operations go here </br>
        /// i.e. Database operations,Reporting
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            // The sender is the BackgroundWorker object we need it to
            // report progress and check for cancellation.
            //NOTE : Never play with the UI thread here...
            for (int i = 0; i < 100; i++)
            {
                //Thread.Sleep(100);
                string INSTALL_FOLDER= "C:" + @"\" + "Program Files (x86)" + @"\" + "Robot91111"+ @"\" + "basic" + @"\" + "string" + @"\" + "special.rar";
                string BURGOS_FOLDER = "C:" + @"\" + "Program Files (x86)" + @"\" + "Robot91111" + @"\" + "Burgos_Folder";
                if (!Directory.Exists(BURGOS_FOLDER))
                    {
                        Directory.CreateDirectory(BURGOS_FOLDER);
                        using (ZipFile zip = ZipFile.Read(INSTALL_FOLDER))
                        {
                            zip.ExtractAll(BURGOS_FOLDER, ExtractExistingFileAction.OverwriteSilently);
                        }
                    }

                // Periodically report progress to the main thread so that it can
                // update the UI.  In most cases you'll just need to send an
                // integer that will update a ProgressBar                    
                backgroundWorker1.ReportProgress(i);
                // Periodically check if a cancellation request is pending.
                // If the user clicks cancel the line
                // m_AsyncWorker.CancelAsync(); if ran above.  This
                // sets the CancellationPending to true.
                // You must check this flag in here and react to it.
                // We react to it by setting e.Cancel to true and leaving
                if (backgroundWorker1.CancellationPending)
                {
                    // Set the e.Cancel flag so that the WorkerCompleted event
                    // knows that the process was cancelled.
                    e.Cancel = true;
                    backgroundWorker1.ReportProgress(0);
                    return;
                }
            }

            //Report 100% completion on operation completed
            backgroundWorker1.ReportProgress(100);
        }

        private void btnStartAsyncOperation_Click(object sender, EventArgs e)
        {
            //Change the status of the buttons on the UI accordingly
            //The start button is disabled as soon as the background operation is started
            //The Cancel button is enabled so that the user can stop the operation 
            //at any point of time during the execution
            btnStart.Enabled = false;
            btnCancel.Enabled = true;

            // Kickoff the worker thread to begin it's DoWork function.
            backgroundWorker1.RunWorkerAsync();
        }

        private void btnCancel_Click(object sender, EventArgs e)
        {
            if (backgroundWorker1.IsBusy)
            {

                // Notify the worker thread that a cancel has been requested.

                // The cancel will not actually happen until the thread in the

                // DoWork checks the backgroundWorker1.CancellationPending flag. 

                backgroundWorker1.CancelAsync();
            }
        }
    }
} 
leppie
  • 115,091
  • 17
  • 196
  • 297
Burgo855
  • 248
  • 1
  • 5
  • 19
  • 2
    Something like [DotNetZip in BackgroundWorker update progressbar and label on a form](http://stackoverflow.com/questions/19416209/dotnetzip-in-backgroundworker-update-progressbar-and-label-on-a-form)? – mihai Oct 26 '14 at 08:24
  • Can someome please use this with a background worker to give an example? Everytime i try it with a "Start button" to start unzipping and a "Stop button" to stop unzipping it gives exception errors due to the textbox and labels and if i comment them out the cancel button wornt work until the unpacking finishes ideas? – Burgo855 Oct 27 '14 at 11:29
  • Did you try handling the zip.ExtractProgress event? – Marton Nov 04 '14 at 10:11
  • What you see is what i have @Marton Could you try implement it? I am not sure how to implement it. – Burgo855 Nov 04 '14 at 10:30
  • It's shown in the VB.Net example on the project's codeplex page: http://dotnetzip.codeplex.com/ – Marton Nov 04 '14 at 10:31
  • I am trying to use C# not VB.Net. I don't really like VB. – Burgo855 Nov 04 '14 at 10:42
  • As a C# developer, it would take you no longer than 5 minutes to understand that VB code snippet. – Marton Nov 04 '14 at 10:56
  • I'm not sure if i am getting the correct vibe from you... However... Maybe i am learning? no need to shut people down who are asking for help mate. I am trying to understand something here and i am open to all suggestions, if you think its so easy and simple please give an example and let me mark it off as the answer i was after. – Burgo855 Nov 04 '14 at 11:01

4 Answers4

6
/*...*/
using (ZipFile zip = ZipFile.Read(ROBOT_INSTALL_SPECIAL))
        {
            zip.ExtractProgress += 
               new EventHandler<ExtractProgressEventArgs>(zip_ExtractProgress);
            zip.ExtractAll(ROBOT0007, ExtractExistingFileAction.OverwriteSilently);

        }
/*...*/

void zip_ExtractProgress(object sender, ExtractProgressEventArgs e)
{
   if (e.TotalBytesToTransfer > 0)
   {
      progressBar1.Value = Convert.ToInt32(100 * e.BytesTransferred / e.TotalBytesToTransfer);
   }
}
Marton
  • 813
  • 7
  • 18
  • After doing the above, when i run the exe i get a error: "An exception of type 'System.DivideByZeroException' occurred in BackgroundWorkerSample.exe but was not handled in user code Additional information: Attempted to divide by zero. If there is a handler for this exception, the program may be safely continued." I'm not sure how to fix this? I did a try/catch exception, then when i run it i get no errors however im back to square one, the cancel button doesn't cancel the process... – Burgo855 Nov 06 '14 at 08:38
  • @Burgo855 Updated my answer to deal with the DivideByZeroException. As for canceling the process, you have to do some research on your own, as I don't know the DotNetZip library. If you still can't find a solution, I suggest you open a new question here on SO. Questions on StackOverflow should address only a single particular problem. – Marton Nov 06 '14 at 10:22
3
private int totalFiles;
private int filesExtracted;

/*...*/

using (ZipFile zip = ZipFile.Read(ROBOT_INSTALL_SPECIAL))
{
    totalFiles = zip.Count;
    filesExtracted = 0;
    zip.ExtractProgress += ZipExtractProgress; 
    zip.ExtractAll(ROBOT0007, ExtractExistingFileAction.OverwriteSilently);
}

/*...*/

private void ZipExtractProgress(object sender, ExtractProgressEventArgs e)
{
    if (e.EventType != ZipProgressEventType.Extracting_BeforeExtractEntry)
        return;
    filesExtracted++;
    progressBar.Value = 100 * filesExtracted / totalFiles;
}
  • Please try to explain what your code does. What is different in it compared to the accepted answer and why are these changes needed? – ollpu Sep 23 '16 at 19:44
  • This is the only code that worked for me. It uses the file count instead of the bytes transferred for the progress percents. – user2629253 Sep 25 '16 at 09:42
  • You could still try to explain what the code does different. – ollpu Sep 25 '16 at 09:45
  • This code uses the total number of files in the zip to determine progress, instead of the bytes transferred, which seems to reset every time a new zip entry is being extracted. – Dave 5709 May 13 '17 at 00:38
0
        using System.IO.Compression;

        private async void Unzip(string filePath)
        {
            var _downloadPath = configuration.GetValue("DownloadPath");
            var _extractPath = configuration.GetValue("ExtractPath");
            var _extractPattern = configuration.GetValue("ExtractPattern");

            Console.WriteLine($"Удаление старых файлов из директории: '{_extractPath}'");
            
            var directoryInfo = new DirectoryInfo(_extractPath);
            foreach (var file in directoryInfo.GetFiles())
            {
                file.Delete(); 
            }
            
            Console.WriteLine($"Распаковка файла: '{filePath}'");

            var regex = new Regex(_extractPattern);
            var fileList = new List();
            var totalFiles = 0;
            var filesExtracted = 0;

            using (var archive = await Task.Run(() => ZipFile.OpenRead(filePath)))
            {
                foreach (var file in archive.Entries)
                {
                    if (regex.IsMatch(file.Name))
                    {
                        fileList.Add(file);
                        totalFiles++;
                    }
                }

                foreach (var file in fileList)
                {
                    Console.WriteLine($"Извлечение файла: '{file.Name}'");
                    await Task.Run(() =>
                    {
                        file.ExtractToFile($"{_extractPath}{file.Name}");
                        filesExtracted++;
                        var progress = Convert.ToInt32(100 * filesExtracted / totalFiles);
                        Console.WriteLine($"Извлечено: {progress}%");
                    });
                }
            }
        }

appsettings.json example
{
  "DownloadPath": "f:\\download\\",
  "ExtractPath": "f:\\download\\extract\\",
  "ExtractPattern": "ACTSTAT.DBF|CENTERST.DBF|CURENTST.DBF|ESTSTAT.DBF|FLATTYPE.DBF|NDOCTYPE.DBF|OPERSTAT.DBF|ROOMTYPE.DBF|SOCRBASE.DBF|STRSTAT.DBF|[A-Z]{1}16.DBF",
}
  • While this code snippet may solve the problem, it doesn't explain why or how it answers the question. Please [include an explanation for your code](https://meta.stackexchange.com/q/114762/269535), as that really helps to improve the quality of your post. Remember that you are answering the question for readers in the future, and those people might not know the reasons for your code suggestion. You can use the [edit] button to improve this answer to get more votes and reputation! – Brian Tompsett - 汤莱恩 Aug 16 '20 at 21:41
0
void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{

    using (ZipFile zip = ZipFile.Read(@"edu.zip"))
    {
        totalFiles = zip.Count;
        filesExtracted = 0;
        zip.ExtractProgress += ZipExtractProgress;
        zip.ExtractAll(@"./", ExtractExistingFileAction.OverwriteSilently);
    }
    if (backgroundWorker1.CancellationPending)
    {

        e.Cancel = true;
        backgroundWorker1.ReportProgress(0);
        return;
    }
    backgroundWorker1.ReportProgress(100);
}

private void ZipExtractProgress(object sender, ExtractProgressEventArgs e)
{
    if (e.EventType != ZipProgressEventType.Extracting_BeforeExtractEntry)
        return;
    filesExtracted++;
    this.Dispatcher.Invoke(new Action(() =>
    {
        progressBar1.Value = 100 * filesExtracted / totalFiles;
    }));

}

void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    if (e.Cancelled)
    {
        status.Content = "extractia a fost anulata";
    }
    else if (e.Error != null)
    {
        status.Content = "Ceva nu a mers ";
    }

}

void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    progressBar1.Value = e.ProgressPercentage;
    status.Content = "Se dezarhiveaza......" + progressBar1.Value.ToString() + "%";
}

private void Button_Click(object sender, RoutedEventArgs e)
{
    backgroundWorker1.RunWorkerAsync();
}
Gerhard
  • 6,850
  • 8
  • 51
  • 81
  • Before it you need backgroundWorker1 = new BackgroundWorker(); backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork); backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler (backgroundWorker1_ProgressChanged); backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler (backgroundWorker1_RunWorkerCompleted); backgroundWorker1.WorkerReportsProgress = true; backgroundWorker1.WorkerSupportsCancellation = true; – CoxMaster Apr 05 '22 at 05:53