Progress bar while copying files with Java

11,887

Solution 1

I have found a solution that works. It still has some minor glitches, but mainly with unimportant details. Here is what I have now, and it seems to be working.

class Task extends SwingWorker<Void, Void>{
    @Override
    public Void doInBackground(){
        setProgress(0);

        //Create Backup Directory
        Date date = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("MM-dd-yyyy_HMMSS");
        String timestamp = sdf.format(date);
        backupDir = backupDrive + ":\\" + "Backup_" + timestamp;
        File backupDirectory = new File(backupDir);
        backupDirectory.mkdir();

        //Copy Files
        //Main directories
        String pics = mainDrive + ":\\Users\\" + username + "\\Pictures\\";
        String docs = mainDrive + ":\\Users\\" + username + "\\Documents\\";
        String vids = mainDrive + ":\\Users\\" + username + "\\Videos\\";
        String musc = mainDrive + ":\\Users\\" + username + "\\Music\\";
        //Backup directories
        String bkPics = backupDir + "\\Pictures\\";
        String bkDocs = backupDir + "\\Documents\\";
        String bkVids = backupDir + "\\Documents\\";
        String bkMusc = backupDir + "\\Pictures\\";

        String[] directories = {pics, docs, vids, musc};
        String[] bkDirectories = {bkPics, bkDocs, bkVids, bkMusc};

        //Loop through directories and copy files
        for (int i = 0; i < directories.length; i++){
            File dir = new File(directories[i]);
            File dest = new File(bkDirectories[i]);
            for(File file: dir.listFiles()){
                try{
                    if(file.isFile()){
                        FileUtils.copyFileToDirectory(file, dest);
                        txtCopiedDirs.append(file.getAbsolutePath() + "\n");
                    } else if (file.isDirectory()){
                        FileUtils.copyDirectoryToDirectory(file, dest);
                        txtCopiedDirs.append(file.getAbsolutePath() + "\n");
                    }
                    if(getDirSize(file) >= ONE_PERCENT){
                        currentPercent = getDirSize(file)/ONE_PERCENT;
                        progressBar.setValue((int)currentPercent);
                        currentSize = 0;
                    } else {
                        currentSize = currentSize + getDirSize(file);
                        if(currentSize >= ONE_PERCENT){
                            currentPercent = currentSize/ONE_PERCENT;
                            currentPercent++;
                            progressBar.setValue((int)currentPercent);
                            currentSize = 0;
                        }
                    }
                } catch (IOException e){
                    e.printStackTrace();
                }
            }
        }

        return null;
    }
    @Override
    public void done(){
        closeWindow();
    }
}

This class is contained within the main class, and is started using the following code:

Task task = new Task();
task.execute();

This is called immediately after the frame is created and set visible.

Now, as I mentioned this still isn't perfect. For example, the check to see if it's a file or a directory before copying it should be replaced with a recursive function, so that it loops through each individual file, rather than just copy the directory if it's a full directory. This will allow for more accurate updating of the progress bar and will show the individual files being copied in the text area, rather than files and directories.

Anyway, I just wanted to post this code to show what has worked for me, without completely redoing what I had. I will keep working on it though, and post any important updates that may help future readers.

Thanks everyone for the responses, they've helped a lot!

Solution 2

I'm assuming you want a graphical progress bar, and not a console based (tui) solution. Have you read this Swing tutorial?

EDIT: If you want one tick of your progress bar per file, you simply need to tell the progress bar constructor how many total ticks there are, something like:

progressBar = new JProgressBar(0, allFilesInAllDirectoriesLength);

and organize your for loop to work on each file, instead of looping on the directories.

Solution 3

Here is my idea using Java 8 and apache FileUtils (can be replaced by any other). It simply checks (in separate thread) directory size every n seconds:

FUNCTIONAL INTERFACE:

public interface DirectoryMovementStatusFeedback {
    void notifyStatus(double percentMoved, double speedInMB);
}

SERVICE:

public class FileMovementStatusUpdater {

    private long checkInterval = 2000;
    private boolean interrupted = false;

    public long getCheckInterval() {
        return checkInterval;
    }

    public void setCheckInterval(long checkInterval) {
        this.checkInterval = checkInterval;
    }

    public void monitor(File directory, long desiredSize, DirectoryMovementStatusFeedback feedback) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    double percentageMoved = 0;
                    double lastCheckedSize = 0;
                    do {
                        double currentSize = directory.exists() ? FileUtils.sizeOfDirectory(directory) : 0;
                        double speed = (currentSize - lastCheckedSize) / (1024 * checkInterval);
                        lastCheckedSize = currentSize;
                        percentageMoved = 100 * currentSize / desiredSize;
                        feedback.notifyStatus(percentageMoved, speed);
                        Thread.sleep(checkInterval);
                    } while (percentageMoved < 100 && !interrupted);
                } catch (Exception e) {
                    System.err.println("Directory monitor failed. " + e.getMessage());
                }
            }
        }).start();
    }

    public void stopMonitoring() {
        this.interrupted = true;
    }

USAGE:

    FileMovementStatusUpdater dirStatus = new FileMovementStatusUpdater();
    try {
        dirStatus.monitor(destination, FileUtils.sizeOfDirectory(source), (percent, speed) -> {
            progressBar.setValue(percent);
            speedLabel.setValue(speed+" MB/s");
        });
        FileUtils.moveDirectory(source, destination);
    } catch (Exception e) {
        // something
    }
    dirStatus.stopMonitoring();

Solution 4

This might be more complicated and maybe stupid idea bud,Maybe it helps so i decided to post it. Use your own method to copy file ,check file size ,take 1 percent from it and each time you reach amount of bites/mb/ that represent 1 percent of that file update your progress bar

Im short on time so i will at least paste some code with comments so you get idea of what i think.

//check size of files/dir to copy,before calling method bellow
//then you coud use for loop to do all the work

//make counter of how many mb/bits you already did.

    public void copyDirectory(File sourceLocation , File targetLocation) throws IOException {
        if (sourceLocation.isDirectory()) {
            if (!targetLocation.exists()) {
                targetLocation.mkdir();
            }

            String[] children = sourceLocation.list();
            for (int i=0; i<children.length; i++) {
                copyDirectory(new File(sourceLocation, children[i]),
                        new File(targetLocation, children[i]));
            }
        } else {

            InputStream in = new FileInputStream(sourceLocation);
            OutputStream out = new FileOutputStream(targetLocation);


            // Copy the bits from instream to outstream
            byte[] buf = new byte[1024];
            int len;
            while ((len = in.read(buf)) > 0) {
                out.write(buf, 0, len);
                //increment counter 
                //check if counter is above next 1% of size of your dirs

                //if you are then update progress bar by one percent
            }
            in.close();
            out.close();
        }
    }

This solution is not tested bud this is how i woud start to aproach this problem.

Share:
11,887

Related videos on Youtube

DerStrom8
Author by

DerStrom8

I am an electronics and programming enthusiast with a background in electronics hardware design/manufacturing/repair and various programming languages. I held a job as a software developer programming primarily in Java, Javascript, and HTML but am familiar with other languages such as C, C++, Assembly, and a little bit of VB and VHDL as well. I have also dabbled in writing batch scripts and Android apps but am still in the learning process. I currently work as an Electrical Engineer primarily doing Schematic and PCB design. My work often involves some minor mechanical design as well, namely mechanical definitions for PCBs and enclosures as well as the fabrication and assembly drawings for the boards I design. Passing on knowledge while obtaining more from others is my primary focus as a member here on the StackExchange sites.

Updated on September 26, 2022

Comments

  • DerStrom8
    DerStrom8 over 1 year

    I'm sure this question has been asked before, but none of the answers I found will work very well with my existing code. I'm posting this question in case there's a way to do it without completely redoing what I have so far.

    The idea is to display a very basic progress bar while copying files and directories from one drive to another. I have a class called BasicCopy that is designed to copy the contents of the Pictures, Documents, videos, and Music folders (standard on Windows machines) to folders of the same names within a backup directory on a second drive. Here is the class so far:

    import java.io.File;
    import java.io.IOException;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    import org.apache.commons.io.FileUtils;
    
    public class BasicCopy {
    
        String username = "";
        String mainDrive = "";
        String backupDrive = "";
        String backupDir = "";
        String[] directories;
    
        public BasicCopy(String inDrive, String outDrive, String username){
            mainDrive = inDrive;
            backupDrive = outDrive;
            this.username = username;
    
            createBackupDirectory();
            copyDirectories();
    
            close();
        }
    
        //Create backup directory
        public void createBackupDirectory(){
            Date date = new Date();
            SimpleDateFormat sdf = new SimpleDateFormat("MM-dd-yyyy_HMMSS");
            String timestamp = sdf.format(date);
            backupDir = backupDrive + ":\\" + "Backup " + timestamp;
            File backupDirectory = new File(backupDir);
            backupDirectory.mkdir();
        }
    
        public void copyDirectories(){
            //Main directories
            String pics = mainDrive + ":\\Users\\" + username + "\\Pictures";
            String docs = mainDrive + ":\\Users\\" + username + "\\Documents";
            String vids = mainDrive + ":\\Users\\" + username + "\\Videos";
            String musc = mainDrive + ":\\Users\\" + username + "\\Music";
            //Backup directories
            String bkPics = backupDir + "\\Pictures";
            String bkDocs = backupDir + "\\Documents";
            String bkVids = backupDir + "\\Documents";
            String bkMusc = backupDir + "\\Pictures";
    
            String[] directories = {pics, docs, vids, musc};
            String[] bkDirectories = {bkPics, bkDocs, bkVids, bkMusc};
    
            //Loop through directories and copy files
            for (int i = 0; i < directories.length; i++){
                File src = new File(directories[i]);
                File dest = new File(bkDirectories[i]);
                try{
                    FileUtils.copyDirectory(src, dest);
                } catch (IOException e){
                    e.printStackTrace();
                }
            }
        }
    
        /* Close current process */
        public void close(){
            System.exit(0);
        }
    }
    

    I have a method in a previous class that measures the total size of the directories, so I could pass that in to this class if necessary. However, I currently loop through only the four directories, so I expect I wouldn't be able to increment a progress bar with any higher resolution than 25% per tick. I am wondering if there's a way I might change it so that I can include a progress bar to monitor it, and have it be a little more accurate? Furthermore, I'm not sure if this should be asked in a different thread or not, but this method of file copying takes a very long time. It takes hours to copy 500MB worth of files, and I was wondering if there might be a way to speed it up? That part isn't a priority though. Right now I'm mainly interested in adding in a progress bar. Cheers!

    EDIT:

    After some fiddling I realized I could probably use code similar to this (This exact code may or may not work--I just jotted it down quickly so I wouldn't forget it, it is still untested). This would allow me to update the progress bar for each file copied.

    for (int i = 0; i < directories.length; i++){
        File dir = new File(directories[i]);
        File dest = new File(bkDirectories[i]);
        for(File file: dir.listFiles()){
            try{
                FileUtils.copyDirectory(file, dest);
                //update progress bar here
            } catch (IOException e){
                e.printStackTrace();
            }
        }
    }
    

    EDIT #2:

    I have worked a bit more on the code and I believe I've figured most of it out. The question now is about a SwingWorker, which I believe I'll need in order to run the long-term methods in the background. Otherwise the GUI becomes unresponsive (lots of documentation on this in the Java docs). However, this is where I get stuck. I've only used a SwingWorker once before, and that was mainly with copied code. I am wondering how I could implement that using the following code so that the progress bar (and the rest of the frame) actually appears.

    Updated code:

    import java.awt.Toolkit;
    import java.awt.event.WindowEvent;
    import java.io.File;
    import java.io.IOException;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.border.EmptyBorder;
    import javax.swing.JTextArea;
    import javax.swing.JButton;
    import javax.swing.JProgressBar;
    import javax.swing.JLabel;
    
    import org.apache.commons.io.FileUtils;
    
    @SuppressWarnings("serial")
    public class BasicCopy extends JFrame {
    
        private JPanel contentPane;
        private JTextArea txtCopiedDirs;
        private JButton btnCancel;
        private JProgressBar progressBar;
        private JLabel lblCopying;
        private String mainDrive;
        private String backupDrive;
        private String username;
        private String backupDir;
        long totalSize = 0; //total size of directories/files
        long currentSize = 0;   //current size of files counting up to ONE_PERCENT
        int currentPercent = 0; //current progress in %
        long ONE_PERCENT;       //totalSize / 100
    
        public BasicCopy() {
        }
    
        public BasicCopy(String inDrive, String outDrive, String username, long space){
            mainDrive = inDrive;
            backupDrive = outDrive;
            this.username = username;
            totalSize = space;
            ONE_PERCENT = totalSize/100;
            createGUI();
    
            /*  Should not have these here!
             *  Pretty sure I need a SwingWorker
             */
            createBackupDirectory();
            copyDirectories();
        }
    
        public void createGUI(){
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            setTitle("Backup Progress");
            setBounds(100, 100, 450, 300);
            contentPane = new JPanel();
            contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
            setContentPane(contentPane);
            contentPane.setLayout(null);
    
            txtCopiedDirs = new JTextArea();
            txtCopiedDirs.setBounds(10, 56, 414, 125);
            contentPane.add(txtCopiedDirs);
    
            btnCancel = new JButton("Cancel");
            btnCancel.setBounds(169, 227, 89, 23);
            contentPane.add(btnCancel);
    
            progressBar = new JProgressBar(0, 100);
            progressBar.setBounds(10, 192, 414, 24);
            progressBar.setValue(0);
            contentPane.add(progressBar);
    
            lblCopying = new JLabel("Now backing up your files....");
            lblCopying.setBounds(10, 11, 157, 34);
            contentPane.add(lblCopying);
    
            setVisible(true);
        }
    
        //Create backup directory
        public void createBackupDirectory(){
            Date date = new Date();
            SimpleDateFormat sdf = new SimpleDateFormat("MM-dd-yyyy_HMMSS");
            String timestamp = sdf.format(date);
            backupDir = backupDrive + ":\\" + "Backup " + timestamp;
            File backupDirectory = new File(backupDir);
            backupDirectory.mkdir();
        }
    
        public void copyDirectories(){
            //Main directories
            String pics = mainDrive + ":\\Users\\" + username + "\\Pictures";
            String docs = mainDrive + ":\\Users\\" + username + "\\Documents";
            String vids = mainDrive + ":\\Users\\" + username + "\\Videos";
            String musc = mainDrive + ":\\Users\\" + username + "\\Music";
            //Backup directories
            String bkPics = backupDir + "\\Pictures";
            String bkDocs = backupDir + "\\Documents";
            String bkVids = backupDir + "\\Documents";
            String bkMusc = backupDir + "\\Pictures";
    
            String[] directories = {pics, docs, vids, musc};
            String[] bkDirectories = {bkPics, bkDocs, bkVids, bkMusc};
    
            //Loop through directories and copy files
            for (int i = 0; i < directories.length; i++){
                File dir = new File(directories[i]);
                File dest = new File(bkDirectories[i]);
                for(File file: dir.listFiles()){
                    try{
                        FileUtils.copyDirectory(file, dest);
                        if(getDirSize(file) >= ONE_PERCENT){
                            currentPercent++;
                            progressBar.setValue(currentPercent);
                            currentSize = 0;
                        } else {
                            currentSize = currentSize + getDirSize(file);
                            if(currentSize >= ONE_PERCENT){
                                currentPercent++;
                                progressBar.setValue(currentPercent);
                                currentSize = 0;
                            }
                        }
                    } catch (IOException e){
                        e.printStackTrace();
                    }
                }
            }
        }
    
        public static Long getDirSize(File directory) {
            long size = 0L;
    
            if (directory.listFiles() != null){       
                for (File file : directory.listFiles()) {
                    size += file.isDirectory() ? getDirSize(file) : file.length();
                }
            }
            return size;
        }
    
        /* Close current window */
        public void closeWindow() {
            WindowEvent close = new WindowEvent(this, WindowEvent.WINDOW_CLOSING);
            Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(close);
            System.exit(0);
        }
    }
    
  • DerStrom8
    DerStrom8 over 10 years
    You are correct, I'm looking for a progress bar with a GUI. However, my main question isn't really about creating the progress bar, it's about linking it to my code. I have read that tutorial, by the way, but thanks for the link nonetheless--I may need it for reference later
  • Amir Afghani
    Amir Afghani over 10 years
    You should clarify in your question what you mean by 'linking' the two. Do you mean, you'd like a higher resolution than just one tick per file copy?
  • DerStrom8
    DerStrom8 over 10 years
    One tick per directory, yes. I would rather have one tick per file, for example. I tried to explain that basic concept (though no real detail) in this sentence: "I currently loop through only the four directories, so I expect I wouldn't be able to increment a progress bar with any higher resolution than 25% per tick."
  • Tomas Bisciak
    Tomas Bisciak over 10 years
    @derstrom8 use your own method to copy file check file size take 1 percent from it and each time you reach amount of bites/mb/ that represent 1 percent of that file update your progress bar
  • DerStrom8
    DerStrom8 over 10 years
    Excellent idea @TomasBisciak, Just the kind of ingenuity I was looking for! Not sure I would have every come up with that myself. If you'd like, you're welcome to post that as an answer. I'll try it out, and if I get it to work I'll accept it. Many thanks!
  • DerStrom8
    DerStrom8 over 10 years
    Hmm, actually I have one further question--At what point would I check the total size? It seems I'd only be able to do it once for each iteration of the loop, which is still a 25% resolution?
  • Tomas Bisciak
    Tomas Bisciak over 10 years
    each time your buffer type something take amount and add it to some kind of counter.When you are in range of 1% or past it update your progressbar.You will need to make your own copyDir method.And yes you have to check file size only at start of copying.Not during.While you loop you only keep track of those bytes that already was copyed to another dir.
  • DerStrom8
    DerStrom8 over 10 years
    @AmirAfghani, regarding your edit, the last line is exactly what I'm asking about. I'll need to change my code to loop through the files instead of only the four directories. I have made an edit of my own in my original post that might do just that. I expect it will work (or some form of it, anyway), but I would appreciate another set of eyes!
  • DerStrom8
    DerStrom8 over 10 years
    I have added some newer code to my original post. I now have it looping through each file, rather than the directories, and now I need to implement a SwingWorker so that I can run the methods in the background. I have a comment in my code (where I call the methods to create the backup directory and to copy the directories) showing what needs to be done. Unfortunately I am unfamiliar with the use of SwingWorkers, so I'm hoping someone would be willing to give me a hand with that part. Much obliged!
  • DerStrom8
    DerStrom8 over 10 years
    I know I told you I'd accept your answer, but it turns out it won't quite work for me after all. I gave you an upvote, but I'm about to post my own solution which seems to have worked. There are still a few glitches, but it works, and I wanted to let you guys, and future readers, know what I did. Many thanks for all your help though, you got me thinking outside of the box!
  • Tomas Bisciak
    Tomas Bisciak over 10 years
    Thank you men ,no problem i just wanted to help like we all do here.Glad that you found solution!
  • MadProgrammer
    MadProgrammer about 9 years
    You do realise that this example violates the single thread rules of swing by updating the progress bar directly from with the doInBackground method, which defeats the purpose of the using a SwingWorker. Better to use the property change support and update the progress bar through it