using swingworker when copying files recursively

46 Views Asked by At

I'm having trouble getting swingworker to work when copying files recursively. No matter what I do, my progress bar doesn't update until after all the files are copied. The direction I chose to do is that I calculate the total number of files. I then have a JLabel that lists the number of files copied out of the total number of files. I then want a progressbar to show the percentage of the files copied.

This is my current code that I have (very basic app, i know)

package swingworkertest;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JProgressBar;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.util.List;

import javax.swing.BoxLayout;
import javax.swing.SwingWorker;

public class App extends JFrame {
    private JPanel mainPanel;
    private JButton button;
    private JLabel label;
    private JProgressBar progress;
    
    private int totalFiles = 0;
    private int copiedFiles = 0;
    
    private void getTotalFiles(File src) {
        String[] files = src.list();
        for (int i = 0; i < files.length; i++) {
            if (new File(src + "\\" + files[i]).isDirectory()) {
                getTotalFiles(new File(src + "\\" + files[i]));
            } else {
                totalFiles++;
            }
        }
    }
    
    private void copyFiles(File src, File dst) throws IOException {
        String[] files = src.list();
        
        for (int i = 0; i < files.length; i++) {
            if (new File(src + "\\" + files[i]).isDirectory()) {
                if (Files.notExists(new File(dst + "\\" + files[i]).toPath(), LinkOption.NOFOLLOW_LINKS)) {
                    new File(dst + "\\" + files[i]).mkdir();
                }
                copyFiles(new File(src + "\\" + files[i]), new File(dst + "\\" + files[i]));
            } else {
                Files.copy(new File(src + "\\" + files[i]).toPath(), new File(dst + "\\" + files[i]).toPath());
                copiedFiles++;
                label.setText("Files Copied: " + copiedFiles + "/" + totalFiles);
            }
        }
    }
    
    private void setProgress(int value) {
        progress.setValue(value);
    }
    
    public App(String title) {
        this.setTitle(title);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        String src = "C:\\Users\\Vinny\\Desktop\\workingDirectory";
        String dst = "C:\\Users\\Vinny\\Desktop\\copyToHere";
        
        mainPanel = new JPanel();
        mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
        
        button = new JButton("Copy");
        label = new JLabel("Files Copied: 0/0");
        progress = new JProgressBar();
        progress.setStringPainted(true);
        
        button.addActionListener((e) -> {
            getTotalFiles(new File(src));
            System.out.println(totalFiles);
            SwingWorker<Boolean, Integer> worker = new SwingWorker<>() {
                
                @Override
                protected Boolean doInBackground() throws Exception {
                    // Background Work
                    copyFiles(new File(src), new File(dst));
                    publish(copiedFiles);
                    
                    // Value transmitted to done()
                    return true;
                }
                
                @Override
                protected void process(List<Integer> chunks) {
                    // Process results
                    for (int n : chunks) {
                        int percentage = (int)(n * 100 / totalFiles);
                        setProgress(percentage);
                    }
                }
                
                @Override
                protected void done() {
                    // Finish sequence
                    System.out.println("Files Copied");
                }
            };
            worker.execute();
            
            while (!worker.isDone()) {
                int percentage = (int)(copiedFiles * 100 / totalFiles);
                setProgress(percentage);
            }
        });
        
        
        mainPanel.add(button);
        mainPanel.add(label);
        mainPanel.add(progress);
        
        this.add(mainPanel);
        
        this.pack();
        this.setVisible(true);
    }

}
2

There are 2 best solutions below

0
markspace On

This is almost certainly the problem.

        worker.execute();
        
        while (!worker.isDone()) {
            int percentage = (int)(copiedFiles * 100 / totalFiles);
            setProgress(percentage);
        }

You're updating the progress on the EDT. Basically that loop is holding onto the UI thread, not letting it do other work. You have to update from the back ground thread if you want the EDT to be able to actually execute.

Here's an example from the tutorial, I'd use the publish() and process() methods to update the GUI as the files are read.

https://docs.oracle.com/javase/tutorial/uiswing/concurrency/interim.html

0
g00se On

If you look at the javadoc for SwingWorker you'll see that it has 'built-in' progress functionality. Try the following:

import javax.swing.JFrame;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeEvent;
import javax.swing.JPanel;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JProgressBar;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.util.List;

import javax.swing.BoxLayout;
import javax.swing.SwingWorker;
import javax.swing.SwingUtilities;

public class App extends JFrame {
    private JPanel mainPanel;
    private JButton button;
    private JLabel label;
    private JProgressBar progress;

    private int totalFiles = 0;
    private int copiedFiles = 0;

    public static void main(String[] args) {
        try {
            SwingUtilities.invokeAndWait(new Runnable() {
                public void run() {
                    App f = new App("Test");
                    f.setVisible(true);
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void getTotalFiles(File src) {
        String[] files = src.list();
        for (int i = 0; i < files.length; i++) {
            if (new File(src + "\\" + files[i]).isDirectory()) {
                getTotalFiles(new File(src + "\\" + files[i]));
            } else {
                totalFiles++;
            }
        }
    }

    public App(String title) {
        this.setTitle(title);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        String src = "C:\\Users\\Vinny\\Desktop\\workingDirectory";
        String dst = "C:\\Users\\Vinny\\Desktop\\copyToHere";

        mainPanel = new JPanel();
        mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));

        button = new JButton("Copy");
        label = new JLabel("Files Copied: 0/0");
        progress = new JProgressBar();
        progress.setStringPainted(true);

        button.addActionListener((e) -> {
            getTotalFiles(new File(src));
            System.out.println(totalFiles);
            new FileCopierWorker(src, dst).execute();
        });
        mainPanel.add(button);
        mainPanel.add(label);
        mainPanel.add(progress);

        this.add(mainPanel);

        this.pack();
        this.setVisible(true);
    }

    private class FileCopierWorker extends SwingWorker<Boolean, Integer> implements PropertyChangeListener {
        String src;
        String dst;

        public FileCopierWorker(String src, String dst) {
            this.src = src;
            this.dst = dst;
            addPropertyChangeListener(this);
        }

        private void copyFiles(File src, File dst) throws IOException {
            System.out.println("Starting to copy...");
            String[] files = src.list();

            for (int i = 0; i < files.length; i++) {
                if (new File(src + "/" + files[i]).isDirectory()) {
                    if (Files.notExists(new File(dst + "/" + files[i]).toPath(), LinkOption.NOFOLLOW_LINKS)) {
                        new File(dst + "/" + files[i]).mkdir();
                    }
                    copyFiles(new File(src + "/" + files[i]), new File(dst + "/" + files[i]));
                } else {
                    label.setText("Files Copied: " + copiedFiles + "/" + totalFiles);
                    Files.copy(new File(src + "/" + files[i]).toPath(), new File(dst + "/" + files[i]).toPath());
                    copiedFiles++;
                    int percentage = (int) (copiedFiles * 100 / totalFiles);
                    setProgress(percentage);
                }
            }
            System.out.println("Finished copy");
        }

        @Override
        protected Boolean doInBackground() throws Exception {
            // Background Work
            copyFiles(new File(src), new File(dst));
            return Boolean.TRUE;
        }

        @Override
        protected void done() {
            // Finish sequence
            System.out.println("Files Copied");
        }

        @Override
        public void propertyChange(PropertyChangeEvent evt) {
            System.out.println("Prop change called");
            if ("progress".equals(evt.getPropertyName())) {
                progress.setValue((Integer) evt.getNewValue());
            }
        }

    }
}