Прогресс-бар при копировании файлов с Java
Я уверен, что этот вопрос задавался раньше, но ни один из ответов, которые я нашел, не будет работать очень хорошо с моим существующим кодом. Я отправляю этот вопрос, если есть способ сделать это, не полностью переделывая то, что у меня есть до сих пор.
идея в том, чтобы показать очень простой прогресс-бар при копировании файлов и папок с одного диска на другой. У меня есть класс BasicCopy, который предназначен для копирования содержимого папок Pictures, Documents, videos и Music (стандарт на машинах 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% за тик. Мне интересно, есть ли способ ... можно ли изменить его, чтобы я мог включить индикатор выполнения для его мониторинга и сделать его немного более точным? Кроме того, я не уверен, следует ли это спрашивать в другом потоке или нет, но этот метод копирования файлов занимает очень много времени. Для копирования файлов на 500 МБ требуется несколько часов, и мне было интересно, может ли быть способ ускорить его? Эта часть не является приоритетом. Сейчас я в основном заинтересован в добавлении индикатора прогресса. Ура!
EDIT:
после некоторой возни я понял, что, вероятно, мог бы использовать код, подобный этому (этот точный код может работать или не работать-я просто быстро записал его, чтобы не забыть его, он все еще непроверен). Это позволит мне обновить индикатор выполнения для каждого скопированного файла.
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:
Я немного больше работал над кодом, и я считаю, что я понял большую часть этого. Теперь вопрос о свинг-работнике, который, я думаю, мне понадобится, чтобы запустить долгосрочные методы в фоновом режиме. В противном случае 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);
}
}
4 ответов
Я нашел решение, которое работает. Он все еще имеет некоторые незначительные сбои, но в основном с незначительными деталями. Вот что у меня есть сейчас, и, кажется, это работает.
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();
это вызывается сразу после создания кадра и установки visible.
теперь, как я уже упоминал, это все еще не идеально. Например, проверьте, является ли это файл или каталог перед копированием его следует заменить рекурсивной функцией, чтобы он проходил через каждый отдельный файл, а не просто копировал каталог, если это полный каталог. Это позволит более точно обновлять индикатор выполнения и показывать отдельные файлы, копируемые в текстовой области, а не файлы и каталоги.
в любом случае, я просто хотел опубликовать этот код, чтобы показать, что сработало для меня, не полностью переделывая то, что у меня было. Я буду продолжать работать. это, хотя, и размещать любые важные обновления, которые могут помочь будущим читателям.
спасибо всем за ответы, они очень помогли!
Я предполагаю, что вам нужен графический индикатор выполнения, а не консоль (tui
) решение. Вы читали этот свинг учебник?
EDIT: если вы хотите один тик вашего индикатора выполнения на файл, вам просто нужно сказать конструктору индикатора выполнения, сколько всего тиков, что-то вроде:
progressBar = new JProgressBar(0, allFilesInAllDirectoriesLength);
и организуйте цикл for для работы с каждым файлом, вместо того, чтобы зацикливаться на каталогах.
Это может быть сложнее и,возможно, глупая идея, может быть, это помогает, поэтому я решил опубликовать ее. Используйте свой собственный метод для копирования файла, проверьте размер файла, возьмите 1 процент от него, и каждый раз, когда вы достигаете количества укусов/Мб/, которые представляют 1 процент этого файла, обновите индикатор выполнения
Im короткое время, поэтому я, по крайней мере, вставлю код с комментариями, чтобы вы поняли, что я думаю.
//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();
}
}
это решение не тестируется bud вот как я начинаю подход к этой проблеме.
вот моя идея с использованием Java 8 и Апач пакета 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();