在Java中递归删除目录

437

在Java中有没有一种递归删除整个目录的方法?

通常情况下,可以删除空目录。但是当需要删除带有内容的整个目录时,就不那么简单了。

你如何在Java中删除带有内容的整个目录?


4
如果使用非空目录调用File.delete()方法,应该简单地返回false。 - Ben S
如果您正在使用Java 8,请查看@RoK的答案。 - Robin
26个回答

11
public void deleteRecursive(File path){
    File[] c = path.listFiles();
    System.out.println("Cleaning out folder:" + path.toString());
    for (File file : c){
        if (file.isDirectory()){
            System.out.println("Deleting file:" + file.toString());
            deleteRecursive(file);
            file.delete();
        } else {
            file.delete();
        }
    }
    path.delete();
}

5
增强版,返回布尔值且不重复:http://pastebin.com/PqJyzQUx - Erik Kaplun

9
public static void deleteDirectory(File path) 
{
    if (path == null)
        return;
    if (path.exists())
    {
        for(File f : path.listFiles())
        {
            if(f.isDirectory()) 
            {
                deleteDirectory(f);
                f.delete();
            }
            else
            {
                f.delete();
            }
        }
        path.delete();
    }
}

代码写得很好,但有一个 bug,修复后就能正常运行了。在 deleteDirectory(f) 下的一行 f.delete() 会抛出 NoSuchFileException,因为 deleteDirectory(f) 已经删除了该文件。每个目录都会在传递到 deleteDirectory(f) 后成为路径,并通过 path.delete() 被删除。因此,在 if f.isDirectory 部分中就不需要 f.delete() 了。只需删除 deleteDirectory(f) 下的 f.delete(); 即可正常工作。 - Trieu Nguyen

5

两种使用符号链接和上述代码失败的方法......并且不知道解决方案。

方式一

运行以下命令创建一个测试:

echo test > testfile
mkdir dirtodelete
ln -s badlink dirtodelete/badlinktodelete

在这里,您可以看到您的测试文件和测试目录:

$ ls testfile dirtodelete
testfile

dirtodelete:
linktodelete

然后运行你的commons-io deleteDirectory()。它会崩溃并显示文件未找到。不确定其他示例在这里做什么。Linux的rm命令将简单地删除链接,而在目录上使用rm -r也会这样做。

Exception in thread "main" java.io.FileNotFoundException: File does not exist: /tmp/dirtodelete/linktodelete

第二种方法

运行以下命令创建一个测试:

mkdir testdir
echo test > testdir/testfile
mkdir dirtodelete
ln -s ../testdir dirtodelete/dirlinktodelete

这里你可以看到你的测试文件和测试目录:

$ ls dirtodelete testdir
dirtodelete:
dirlinktodelete

testdir:
testfile

然后运行你的commons-io deleteDirectory()或者其他人发布的示例代码。它不仅会删除目录,还会删除在被删除目录之外的测试文件。(它隐式取消引用目录并删除其内容)。rm -r只会删除链接。你需要使用类似这样的命令来删除取消引用的文件:"find -L dirtodelete -type f -exec rm {} \;"。

$ ls dirtodelete testdir
ls: cannot access dirtodelete: No such file or directory
testdir:

4

一个最佳解决方案,可以始终处理异常,使得从方法抛出的异常应该始终描述该方法试图(但失败了)要做什么:

private void deleteRecursive(File f) throws Exception {
    try {
        if (f.isDirectory()) {
            for (File c : f.listFiles()) {
                deleteRecursive(c);
            }
        }
        if (!f.delete()) {
            throw new Exception("Delete command returned false for file: " + f);
        }
    } 
    catch (Exception e) {
        throw new Exception("Failed to delete the folder: " + f, e);
    }
}

4
您可以使用:org.apache.commons.io.FileUtils.deleteQuietly(destFile); 删除文件,不会抛出异常。如果文件是一个目录,则删除它及其所有子目录。 与 File.delete() 方法的区别在于: 需要删除的目录不必为空。 当无法删除文件或目录时不会抛出任何异常。

3
以下代码递归删除给定文件夹中的所有内容。
boolean deleteDirectory(File directoryToBeDeleted) {
    File[] allContents = directoryToBeDeleted.listFiles();
    if (allContents != null) {
        for (File file : allContents) {
            deleteDirectory(file);
        }
    }
    return directoryToBeDeleted.delete();
}

3

在传统项目中,我需要创建本地Java代码。我会像创建Paulitex代码那样来创建这段代码。请看:

public class FileHelper {

   public static boolean delete(File fileOrFolder) {
      boolean result = true;
      if(fileOrFolder.isDirectory()) {
         for (File file : fileOrFolder.listFiles()) {
            result = result && delete(file);
         }
      }
      result = result && fileOrFolder.delete();
      return result;
   } 
}

并且这是单元测试:

public class FileHelperTest {

    @Before
    public void setup() throws IOException {
       new File("FOLDER_TO_DELETE/SUBFOLDER").mkdirs();
       new File("FOLDER_TO_DELETE/SUBFOLDER_TWO").mkdirs();
       new File("FOLDER_TO_DELETE/SUBFOLDER_TWO/TEST_FILE.txt").createNewFile();
    }

    @Test
    public void deleteFolderWithFiles() {
       File folderToDelete = new File("FOLDER_TO_DELETE");
       Assert.assertTrue(FileHelper.delete(folderToDelete));
       Assert.assertFalse(new File("FOLDER_TO_DELETE").exists());
    }

}

2

这是一个最基本的主方法,它接受命令行参数。你可能需要添加自己的错误检查或根据需要进行修改。

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;

public class DeleteFiles {

/**
 * @param intitial arguments take in a source to read from and a 
 * destination to read to
 */
    public static void main(String[] args)
                     throws FileNotFoundException,IOException {
        File src = new File(args[0]);
        if (!src.exists() ) {
            System.out.println("FAILURE!");
        }else{
            // Gathers files in directory
            File[] a = src.listFiles();
            for (int i = 0; i < a.length; i++) {
                //Sends files to recursive deletion method
                fileDelete(a[i]);
            }
            // Deletes original source folder
            src.delete();
            System.out.println("Success!");
        }
    }

    /**
     * @param srcFile Source file to examine
     * @throws FileNotFoundException if File not found
     * @throws IOException if File not found
     */
    private static void fileDelete(File srcFile)
                     throws FileNotFoundException, IOException {
        // Checks if file is a directory
        if (srcFile.isDirectory()) {
            //Gathers files in directory
            File[] b = srcFile.listFiles();
            for (int i = 0; i < b.length; i++) {
                //Recursively deletes all files and sub-directories
                fileDelete(b[i]);
            }
            // Deletes original sub-directory file
            srcFile.delete();
        } else {
            srcFile.delete();
        }
    }
}

我希望你能受益!

2
"Guava" 提供了一个简单的方法: "MoreFiles.deleteRecursively()"。与许多共享的示例不同,它考虑符号链接,并且默认情况下不会删除提供路径之外的文件。

1
也许解决这个问题的方法是使用erickson答案中的代码重新实现File类的删除方法:
public class MyFile extends File {

  ... <- copy constructor

  public boolean delete() {
    if (f.isDirectory()) {
      for (File c : f.listFiles()) {
        return new MyFile(c).delete();
      }
    } else {
        return f.delete();
    }
  }
}

1
我认为它的实现是为了模仿大多数命令行实用程序(如“rm”,“rmdir”和“del”)的行为。在这两种选择中,当前的实现明显减少了总体上的惊讶(和愤怒)潜力。它不会改变。 - erickson
4
通常我只看到扩展Swing的Java JRE包。通常情况下,扩展其他类(比如java.io.File)是一个糟糕的想法,因为这可能会导致意外行为的发生。 - Eddie

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接