我在编写的程序中遇到了一个非常奇怪的问题,我将其写入将图片,文档,视频和音乐(从Windows文件系统)复制到备份驱动器。我将字符串数组directories[]
设置为等于{picturesDirectory, documentsDirectory, videosDirectory, musicDirectory}
,并使用for循环遍历每个字符串以复制文件。我使用GUI,因此我有一个运行实际复制方法的SwingWorker。但是,当我运行程序时,它只循环“for”循环两次,只复制图片和文档。我认为我不能真正发布一个有用的SSCCE,所以我只是发布我的全班。 inDrive
是主Windows驱动器(默认为“C”),outDrive
是备用驱动器号,username
是Windows用户名,space
是总磁盘空间文件/目录将占用。
package diana;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import javax.swing.text.DefaultCaret;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JButton;
import javax.swing.JProgressBar;
import javax.swing.JLabel;
import javax.swing.SwingWorker;
import org.apache.commons.io.FileUtils;
@SuppressWarnings("serial")
public class BasicCopy extends JFrame {
private JPanel contentPane;
private JPanel bottomPane;
private JTextArea txtCopiedDirs;
private JScrollPane scrollPane;
private JButton btnCancel;
private JProgressBar progressBar;
private JLabel lblCopying;
private JLabel lblProgress;
private static String mainDrive;
private static String backupDrive;
private static String username;
private static String backupDir;
double totalSize = 0; //total size of directories/files
double currentMB = 0; //size of already-copied files
static double currentSize = 0; //current size of files counting up to ONE_PERCENT
static int currentPercent = 0; //current progress in %
static double ONE_PERCENT; //totalSize / 100
private ManipulateDirectories md;
private Task task;
public BasicCopy() {
}
public BasicCopy(String inDrive, String outDrive, String username, long space) {
mainDrive = inDrive;
backupDrive = outDrive;
BasicCopy.username = username;
totalSize = space;
ONE_PERCENT = totalSize / 100;
createGUI();
}
public void createGUI() {
// Create frame
setTitle("Backup Progress");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 500, 350);
// Create panel for text area/scroll pane
contentPane = new JPanel(new BorderLayout());
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
// Create panel for progress bar/cancel button
bottomPane = new JPanel();
bottomPane.setLayout(new BoxLayout(bottomPane, BoxLayout.Y_AXIS));
lblCopying = new JLabel("Now backing up your files....\n");
lblCopying.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
contentPane.add(lblCopying, BorderLayout.NORTH);
// Create text area/scroll pane
txtCopiedDirs = new JTextArea(10, 50);
txtCopiedDirs.setEditable(false);
DefaultCaret caret = (DefaultCaret) txtCopiedDirs.getCaret();
caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);
scrollPane = new JScrollPane(txtCopiedDirs);
contentPane.add(scrollPane, BorderLayout.CENTER);
lblProgress = new JLabel("Progress:");
progressBar = new JProgressBar(0, 100);
progressBar.setStringPainted(true);
progressBar.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
progressBar.setIndeterminate(true);
btnCancel = new JButton("Cancel");
btnCancel.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(null, "Backup Cancelled!");
closeWindow();
}
});
bottomPane.add(lblProgress, Component.LEFT_ALIGNMENT);
bottomPane.add(progressBar, Component.CENTER_ALIGNMENT);
bottomPane.add(btnCancel, Component.RIGHT_ALIGNMENT);
contentPane.add(bottomPane, BorderLayout.SOUTH);
PropertyChangeListener listener = new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent event) {
if ("progress".equals(event.getPropertyName())) {
currentPercent = (int) event.getNewValue();
progressBar.setValue((int) currentPercent);
progressBar.setString(Integer.toString(currentPercent) + "% ("
+ String.format("%.2f", (currentMB / 1048576))
+ "MB of " + String.format("%.2f", (totalSize / 1048576)) + "MB)");
}
}
};
Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
this.setLocation(dim.width / 2 - this.getSize().width / 2, dim.height / 2 - this.getSize().height / 2);
setVisible(true);
task = new Task();
task.addPropertyChangeListener(listener);
task.execute();
}
/**
* Swing Worker class
*/
class Task extends SwingWorker<Void, String> {
public Task() {
md = new ManipulateDirectories(this);
}
@Override
public Void doInBackground() {
md.makeBackupDirectory();
// Directories to be copied
String pics = mainDrive + ":\\Users\\" + username + "\\Pictures\\";
String docs = mainDrive + ":\\Users\\" + username + "\\Documents\\";
String vids = mainDrive + ":\\Users\\" + username + "\\Videos\\";
String musc = mainDrive + ":\\Users\\" + username + "\\Music\\";
File[] directories = { new File(pics), new File(docs), new File(vids), new File(musc) };
/********** THIS ONLY LOOPS THROUGH PICS AND DOCS **********/
for (int i = 0; i < directories.length; i++) {
md.copyDirectory(directories[i], new File(backupDir));
}
/***********************************************************/
return null;
}
@Override
public void process(List<String> chunks) {
for (String path : chunks) {
txtCopiedDirs.append(path);
}
}
@Override
public void done() {
JOptionPane.showMessageDialog(null, "Backup complete!");
closeWindow();
}
public void updateProgress(int tick) {
if (progressBar.isIndeterminate())
progressBar.setIndeterminate(false);
progressBar.setString(Integer.toString(currentPercent) + "% ("
+ String.format("%.2f", (currentMB / 1048576)) + "MB of "
+ String.format("%.2f", (totalSize / 1048576)) + "MB)");
}
public void publishText(String filename) {
publish(filename + "\n");
}
}
public class ManipulateDirectories {
Task task;
public ManipulateDirectories(Task task) {
this.task = task;
}
public void makeBackupDirectory() {
// Create Backup Directory
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("MM-dd-yyyy_HHmmss");
String timestamp = sdf.format(date);
backupDir = backupDrive + ":\\" + "Backup_" + timestamp;
File backupDirectory = new File(backupDir);
backupDirectory.mkdir();
task.updateProgress(0);
}
// Recursive function to loop through and copy individual files
public void copyDirectory(File file, File dest) {
if (file.isFile()) {
try {
FileUtils.copyFileToDirectory(file, dest);
currentMB = currentMB + getDirSize(file);
currentSize = currentSize + getDirSize(file);
if (currentSize >= ONE_PERCENT) {
currentPercent = currentPercent + (int) (currentSize / ONE_PERCENT);
currentSize = currentSize % ONE_PERCENT;
}
task.updateProgress(currentPercent);
task.publishText(file.getAbsolutePath());
} catch (IOException e) {
e.printStackTrace();
}
} else if (file.isDirectory()) {
File newDir = new File(String.format("%s\\%s", dest.getAbsolutePath(), file.getName()));
if (!newDir.exists()) {
newDir.mkdir();
for (File f : file.listFiles()) {
copyDirectory(f, newDir);
}
}
}
}
public Long getDirSize(File file) {
long size = 0L;
if (file.isFile() && file != null) {
size += file.isDirectory() ? getDirSize(file) : file.length();
} else if (file.isDirectory()) {
for (File f : file.listFiles()) {
size += f.isDirectory() ? getDirSize(f) : 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);
}
}
我在for循环中添加了一条注释,只是执行了两次。我放置了一个断点,它在复制文档后永远不会到达那里,因此它必须在此之前调用SwingWorker的done()
方法。
有谁看到可能导致此问题的原因?我真的很讨厌在这里发布我的所有代码,因为据我所知,阅读这些代码往往很痛苦。我希望有人能找到为什么它过早地结束了。非常感谢!
修改
经过进一步调试(根据评论中的一些建议),我发现由于程序在Documents文件夹中寻找“我的音乐”而存在NPE - 一个不存在的“链接”作为目录(因此为空值)。
java.util.concurrent.ExecutionException: java.lang.NullPointerException
at java.util.concurrent.FutureTask$Sync.innerGet(Unknown Source)
at java.util.concurrent.FutureTask.get(Unknown Source)
at javax.swing.SwingWorker.get(Unknown Source)
at diana.BasicCopy$Task.done(BasicCopy.java:181)
at javax.swing.SwingWorker$5.run(Unknown Source)
at javax.swing.SwingWorker$DoSubmitAccumulativeRunnable.run(Unknown Source)
at sun.swing.AccumulativeRunnable.run(Unknown Source)
at javax.swing.SwingWorker$DoSubmitAccumulativeRunnable.actionPerformed(Unknown Source)
at javax.swing.Timer.fireActionPerformed(Unknown Source)
at javax.swing.Timer$DoPostEvent.run(Unknown Source)
at java.awt.event.InvocationEvent.dispatch(Unknown Source)
at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.awt.EventQueue.access$200(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.run(Unknown Source)
Caused by: java.lang.NullPointerException
at diana.BasicCopy$ManipulateDirectories.copyDirectory(BasicCopy.java:243)
at diana.BasicCopy$ManipulateDirectories.copyDirectory(BasicCopy.java:244)
at diana.BasicCopy$Task.doInBackground(BasicCopy.java:166)
at diana.BasicCopy$Task.doInBackground(BasicCopy.java:1)
at javax.swing.SwingWorker$1.call(Unknown Source)
at java.util.concurrent.FutureTask$Sync.innerRun(Unknown Source)
at java.util.concurrent.FutureTask.run(Unknown Source)
at javax.swing.SwingWorker.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
由于我使用file.isFile()和file.isDirectory()检查,因此我不必担心这一点。根据javadocs,如果文件是文件/目录并且存在,则它们仅返回true。我假设(可能是愚蠢的)它应该跳过像我的音乐这样的东西,因为它不作为目录存在?
答案 0 :(得分:2)
问题是现有的java.io.File
API无法处理符号链接(或者他们在Windows中称之为的内容)。像“我的音乐”这样的目录实际上并不是传统意义上的文件/目录,而是指向另一个文件/目录的特殊标记。
为了克服这个问题,您需要查看Java 7中提供的新Paths/Files API
有关详细信息,请尝试查看Links, Symbolic or Otherwise
答案 1 :(得分:0)
我找到了答案。这是我在这个帖子中提出的同样问题,并且认为解决方案是别的:Java program to calculate directory size keeps throwing NPE
问题是file.listFiles()在文件路径为C:\ Users \ user \ Documents \ My Music时运行时为null。要修复它,我只需在copyDirectory方法底部的“for”循环之前输入一个if语句来检查file.listFiles()!= null:
if (file.listFiles() != null){
for (File f : file.listFiles()) {
copyDirectory(f, newDir);
}
}
我很抱歉在这里张贴这个,显然我已经遇到了另一个线程中回答的相同问题。希望这有助于某人。感谢大家的建议,感谢他们。