ZIP4J Unzipping/Extraction with ProgressBar / Progress Monitor

550 Views Asked by At

My question is, how do we achieve putting some progress bar or progress monitoring in unzipping zip file using ZIP4j library and the .extractAll method. I've done a research about this but I did not find any solution for this or maybe I didn't meet the right answer on internet, and I'm here now to ask anyone on how to do it. I'm using GUI Swing framework for testing only. I hope someone can lend their hand answering it. Thank you!

2

There are 2 best solutions below

3
Abra On

I presume you are referring to this Zip4J.
Here is a demo Swing app that hopefully does what you want.

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.io.File;
import java.util.List;

import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JTextField;
import javax.swing.SwingWorker;

import net.lingala.zip4j.ZipFile;
import net.lingala.zip4j.progress.ProgressMonitor;

public class Zip4jGui implements Runnable {
    private JFileChooser  fileChooser;
    private JFrame  frame;
    private JProgressBar  progressBar;
    private JTextField  zipFileTextField;

    @Override
    public void run() {
        createAndDisplayGui();
    }

    private void browseFiles(ActionEvent event) {
        if (fileChooser == null) {
            fileChooser = new JFileChooser();
        }
        int choice = fileChooser.showOpenDialog(frame);
        if (choice == JFileChooser.APPROVE_OPTION) {
            File zipFile = fileChooser.getSelectedFile();
            zipFileTextField.setText(zipFile.getAbsolutePath());
        }
    }

    private void createAndDisplayGui() {
        frame = new JFrame("Zip4J");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(createProgressPanel(), BorderLayout.PAGE_START);
        frame.add(createFilePanel(), BorderLayout.CENTER);
        frame.add(createButtonsPanel(), BorderLayout.PAGE_END);
        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
    }

    private JPanel createButtonsPanel() {
        JPanel buttonsPanel = new JPanel();
        JButton extractButton = new JButton("Extract");
        extractButton.addActionListener(this::extractZip);
        buttonsPanel.add(extractButton);
        return buttonsPanel;
    }

    private JPanel createFilePanel() {
        JPanel filePanel = new JPanel();
        JLabel zipFileLabel = new JLabel("ZIP File");
        filePanel.add(zipFileLabel);
        zipFileTextField = new JTextField(20);
        filePanel.add(zipFileTextField);
        JButton browseButton = new JButton("Browse...");
        browseButton.addActionListener(this::browseFiles);
        filePanel.add(browseButton);
        return filePanel;
    }

    private JPanel createProgressPanel() {
        JPanel progressPanel = new JPanel();
        JLabel progressLabel = new JLabel("Progress");
        progressPanel.add(progressLabel);
        progressBar = new JProgressBar(0, 100);
        progressPanel.add(progressBar);
        return progressPanel;
    }

    private void extractZip(ActionEvent event) {
        XtrctTsk task = new XtrctTsk();
        task.execute();
    }

    private class XtrctTsk extends SwingWorker<Void, Integer> {
        private ProgressMonitor  progressMonitor;

        @Override
        protected Void doInBackground() throws Exception {
            String path = zipFileTextField.getText();
            File f = new File(path);
            try (ZipFile zipFile = new ZipFile(f)) {
                progressMonitor = zipFile.getProgressMonitor();
                zipFile.setRunInThread(true);
                File dir = f.getParentFile();
                zipFile.extractAll(dir.getAbsolutePath());
                while (!progressMonitor.getState().equals(ProgressMonitor.State.READY)) {
                    int done = progressMonitor.getPercentDone();
                    System.out.println("done: " + done);
                    publish(done);
                    try {
                        Thread.sleep(100);
                    }
                    catch (InterruptedException xInterrupted) {
                        // Safe to ignore (I think).
                        // xInterrupted.printStackTrace();
                    }
                }
            }
            return null;
        }

        protected void done() {
            progressBar.setValue(100);
            JOptionPane.showMessageDialog(frame,
                                          "Extraction ended.",
                                          progressMonitor.getResult().toString(),
                                          JOptionPane.INFORMATION_MESSAGE);
        }

        protected void process(List<Integer> percents) {
            System.out.println("percents = " + percents);
            if (percents != null) {
                int size = percents.size();
                System.out.println("size = " + size);
                if (size > 0) {
                    progressBar.setValue(percents.get(size - 1));
                }
            }
        }
    }

    public static void main(String[] args) {
        Zip4jGui instance = new Zip4jGui();
        EventQueue.invokeLater(instance);
    }
}

This is how it looks when I run it.

screen capture

Refer to the following.

0
MadProgrammer On

To my surprise, Zip4J actually has a reasonable concept of a "progression"!

There's a number of ways you might make this work with Swing, but probably the easiest is to use a SwingWorker, which also has a concept of "progression"

See...

for more details

Runnable example...

import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingWorker;
import javax.swing.border.EmptyBorder;
import net.lingala.zip4j.ZipFile;
import net.lingala.zip4j.exception.ZipException;
import net.lingala.zip4j.progress.ProgressMonitor;

public class Main {

    public static void main(String[] args) {
        new Main();
    }

    public Main() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    JFrame frame = new JFrame();
                    frame.add(new TestPane());
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                } catch (ZipException ex) {
                    Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        });
    }

    public class TestPane extends JPanel {

        private JProgressBar progressBar;

        public TestPane() throws ZipException {
            progressBar = new JProgressBar();
            setBorder(new EmptyBorder(32, 32, 32, 32));
            setLayout(new GridBagLayout());
            add(progressBar);

            ZipFile zipFile = new ZipFile(new File("Test.zip"));
            ProgressMonitor monitor = zipFile.getProgressMonitor();
            zipFile.setRunInThread(true);
            Zip4JZorker worker = new Zip4JZorker(monitor);
            worker.addPropertyChangeListener(new PropertyChangeListener() {
                @Override
                public void propertyChange(PropertyChangeEvent evt) {
                    String propertyName = evt.getPropertyName();
                    Object newValue = evt.getNewValue();
                    System.out.println(propertyName + "/" + newValue);
                    if ("progress".equals(propertyName) && newValue instanceof Integer) {
                        int progress = (int) newValue;
                        progressBar.setValue(progress);
                    } else if ("state".equals(propertyName) && newValue instanceof SwingWorker.StateValue) {
                        SwingWorker.StateValue state = (SwingWorker.StateValue) newValue;
                        if (state == SwingWorker.StateValue.DONE) {
                            try {
                                ((SwingWorker)evt.getSource()).get();
                            } catch (Exception ex) {
                                ex.printStackTrace();
                                JOptionPane.showMessageDialog(TestPane.this, "Operation failed", "Error", JOptionPane.ERROR_MESSAGE);
                            }
                        }
                    }
                }
            });
            zipFile.addFolder(new File("/some/folder/you/want/zipped"));
            worker.execute();
        }

    }

    public class Zip4JZorker extends SwingWorker<Void, Void> {

        private ProgressMonitor progressMonitor;

        public Zip4JZorker(ProgressMonitor progressMonitor) {
            this.progressMonitor = progressMonitor;
        }

        @Override
        protected Void doInBackground() throws Exception {
            while (!progressMonitor.getState().equals(ProgressMonitor.State.READY)) {
                //System.out.println("Percentage done: " + progressMonitor.getPercentDone());
                //System.out.println("Current file: " + progressMonitor.getFileName());
                //System.out.println("Current task: " + progressMonitor.getCurrentTask());
                // You could pass on some information to the UI via the
                // publish method
                setProgress(progressMonitor.getPercentDone());
                Thread.sleep(100);
            }
            // Again, you could pass on additional information to the
            // UI via the publish method
            if (progressMonitor.getResult().equals(ProgressMonitor.Result.SUCCESS)) {
                System.out.println("Successfully added folder to zip");
            } else if (progressMonitor.getResult().equals(ProgressMonitor.Result.ERROR)) {
                throw progressMonitor.getException();
            } else if (progressMonitor.getResult().equals(ProgressMonitor.Result.CANCELLED)) {
                System.out.println("Task cancelled");
            }
            return null;
        }

    }
}

Okay, before anyone asks, what's the difference between this an @Abra's answer, the basic answer is, I use setProgress over publish.

publish can then be used to provide additional details, like the current file being processed. This is just a different way to approach the same problem