使用Java复制文件时的进度条

我确定之前已经问过这个问题,但我找到的答案都不能与我现有的代码很好地协调。 我发布这个问题,以防有一种方法可以做到这一点,而不是完全重做我到目前为止所拥有的。

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

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

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

 class Task extends SwingWorker{ @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(); 

在创建框架并立即设置后立即调用此方法。

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

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

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

我假设你想要一个图形进度条,而不是一个基于控制台的( tui )解决方案。 你读过这个Swing 教程吗?

编辑:如果你想要每个文件的进度条一个勾号,你只需要告诉进度条构造函数有多少总刻度,如:

 progressBar = new JProgressBar(0, allFilesInAllDirectoriesLength); 

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

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

我的时间很短,所以我至少会粘贴一些带注释的代码,这样你就可以了解我的想法。

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

这个解决方案没有测试芽这是我如何开始解决这个问题。

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

function接口:

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