【问题标题】:Progress bar while copying files with Java使用 Java 复制文件时的进度条
【发布时间】:2014-01-13 01:42:17
【问题描述】:

我确定以前有人问过这个问题,但我找到的答案都不能很好地适用于我现有的代码。我发布这个问题以防万一有办法在不完全重做我目前所做的事情的情况下做到这一点。

这个想法是在将文件和目录从一个驱动器复制到另一个驱动器时显示一个非常基本的进度条。我有一个名为 BasicCopy 的类,旨在将图片、文档、视频和音乐文件夹(Windows 机器上的标准)的内容复制到第二个驱动器上的备份目录中的同名文件夹。这是到目前为止的课程:

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);
    }
}

我在前一个类中有一个方法可以测量目录的总大小,所以如果需要,我可以将它传递给这个类。但是,我目前只遍历四个目录,所以我希望我无法以高于 25% 的分辨率递增进度条。我想知道是否有一种方法可以更改它,以便我可以包含一个进度条来监视它,并让它更准确一点?此外,我不确定是否应该在不同的线程中询问,但这种文件复制方法需要很长时间。复制 500MB 的文件需要几个小时,我想知道是否有办法加快速度?不过,那部分不是优先事项。现在我主要对添加进度条感兴趣。干杯!

编辑:

经过一番摆弄后,我意识到我可能可以使用类似的代码(这个确切的代码可能有效,也可能无效——我只是快速记下它,所以我不会忘记它,它仍然未经测试)。这将允许我更新每个复制文件的进度条。

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();
        }
    }
}

编辑 #2:

我在代码上做了更多的工作,我相信我已经弄清楚了大部分。现在的问题是关于 SwingWorker,我相信我需要它才能在后台运行长期方法。否则 GUI 会变得无响应(Java 文档中有很多关于此的文档)。但是,这就是我卡住的地方。我以前只使用过一次 SwingWorker,主要是复制代码。我想知道如何使用以下代码来实现它,以便实际出现进度条(以及框架的其余部分)。

更新代码:

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);
    }
}

【问题讨论】:

  • “我找到的答案都不能很好地适用于我现有的代码” 为什么不呢?
  • 它们都需要完全重写,需要不同的方法来循环文件、创建目录等。我按照我的方式设置循环是有原因的,我真的希望我能调整进度条以使用它,因为我的代码运行正常。
  • 1) btnCancel.setBounds(169, 227, 89, 23); 您从哪里获得代码示例?底线是那是非常糟糕的代码,不应该这样做。我想知道是什么资源导致人们走上了这条错误的道路。 2) 作为一般建议:不要阻塞 EDT(事件调度线程)——当这种情况发生时,GUI 将“冻结”。而不是调用Thread.sleep(n) 实现一个Swing Timer 用于重复任务或一个SwingWorker 用于长时间运行的任务。有关详细信息,请参阅Concurrency in Swing
  • “我真的希望我可以调整进度条来使用它”我真的希望圣诞节能有一匹小马。我们并不总是得到我们想要的..
  • 哈哈,是的,我明白我可能不明白,我意识到我可能不得不完全重写,但我发布了这个问题,以防万一我有办法做到这一点使用我现有的代码。无论如何,1) 部分布局(例如您在之前的评论中提到的取消按钮代码)是使用 WindowBuilder 创建的。我将按钮放在布局选项卡上我想要的位置,它会自动为我生成代码。 2) 我知道我不应该阻止 EDT,这就是为什么我在最近的编辑中发布了代码并附有评论;我需要应用一个 swingworker 而不是在 EDT 中调用方法

标签: java swing file copy jprogressbar


【解决方案1】:

我假设您需要图形进度条,而不是基于控制台 (tui) 的解决方案。你读过这篇 Swing tutorial 吗?

编辑:如果您希望每个文件的进度条有一个刻度,您只需告诉进度条构造函数总共有多少刻度,例如:

progressBar = new JProgressBar(0, allFilesInAllDirectoriesLength);

并组织你的 for 循环来处理每个文件,而不是在目录上循环。

【讨论】:

  • 你说得对,我正在寻找带有 GUI 的进度条。但是,我的主要问题并不是关于创建进度条,而是关于将其链接到我的代码。顺便说一句,我已经阅读了该教程,但是仍然感谢您的链接-我可能需要它供以后参考
  • 您应该在问题中阐明“链接”两者的含义。你的意思是,你想要一个比每个文件副本只有一个刻度更高的分辨率?
  • 每个目录打勾,是的。例如,我宁愿每个文件有一个刻度。我试图在这句话中解释这个基本概念(尽管没有真正的细节):“我目前只循环浏览四个目录,所以我希望我无法以任何高于 25% 的分辨率递增进度条。 。”
  • @derstrom8 使用您自己的方法来复制文件检查文件大小从中提取 1% 并且每次达到代表该文件 1% 的比特数/mb/ 时更新您的进度条
  • 好主意@TomasBisciak,正是我正在寻找的那种独创性!不确定我会自己想出那个。如果您愿意,欢迎您将其发布为答案。我会尝试一下,如果我让它工作,我会接受它。非常感谢!
【解决方案2】:

这可能更复杂,也许是愚蠢的想法,也许它有帮助,所以我决定发布它。 使用您自己的方法复制文件,检查文件大小,从中提取 1%,每次达到代表该文件 1% 的咬数/mb/ 时,更新您的进度条

我的时间很短,所以我至少会用 cmets 粘贴一些代码,以便您了解我的想法。

//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();
        }
    }

这个解决方案没有经过测试,这就是我开始解决这个问题的方式。

【讨论】:

  • 我知道我告诉过你我会接受你的回答,但事实证明它对我来说根本不起作用。我给了你一个赞成票,但我即将发布我自己的似乎有效的解决方案。仍然有一些小故障,但它有效,我想让你们和未来的读者知道我做了什么。非常感谢您的所有帮助,您让我跳出框框思考!
  • 谢谢你们,没问题,我只是想像我们一样在这里提供帮助。很高兴您找到了解决方案!
【解决方案3】:

我找到了一个可行的解决方案。它仍然有一些小故障,但主要是不重要的细节。这是我现在所拥有的,它似乎正在工作。

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();
    }
}

该类包含在主类中,使用以下代码启动:

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

在创建框架并设置为可见后立即调用它。

现在,正如我所提到的,这仍然不完美。例如,在复制之前检查它是文件还是目录应该用递归函数替换,以便循环遍历每个单独的文件,而不是在目录完整时复制目录。这将允许更准确地更新进度条,并将在文本区域中显示正在复制的各个文件,而不是文件和目录。

无论如何,我只是想发布这段代码来展示对我有用的东西,而不是完全重做我拥有的东西。不过,我会继续努力,并发布任何可能对未来读者有所帮助的重要更新。

感谢大家的回复,他们帮了很多忙!

【讨论】:

  • 你确实意识到这个例子违反了swing的单线程规则,直接使用doInBackground方法更新进度条,这违背了使用SwingWorker的目的。最好使用属性更改支持并通过它更新进度条
【解决方案4】:

这是我使用 Java 8 和 apache FileUtils 的想法(可以替换为任何其他)。它只是每 n 秒检查一次(在单独的线程中)目录大小:

功能界面:

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

服务:

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;
    }

用法:

    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();

【讨论】:

    猜你喜欢
    • 2012-12-23
    • 2015-12-12
    • 1970-01-01
    • 2011-08-28
    • 1970-01-01
    • 2012-05-18
    • 2012-03-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多