For循环仅在复制文件时执行两次

时间:2013-12-30 18:24:44

标签: java swing for-loop swingworker

我在编写的程序中遇到了一个非常奇怪的问题,我将其写入将图片,文档,视频和音乐(从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。我假设(可能是愚蠢的)它应该跳过像我的音乐这样的东西,因为它不作为目录存在?

2 个答案:

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

我很抱歉在这里张贴这个,显然我已经遇到了另一个线程中回答的相同问题。希望这有助于某人。感谢大家的建议,感谢他们。