在Java中是否有一种方法可以递归地删除整个目录?

在正常情况下,可以删除空目录。 然而,当涉及到删除包含内容的整个目录时,就不再那么简单了。

如何在Java中删除包含内容的整个目录?


当前回答

使用符号链接和上面的代码失败的两种方式…也不知道怎么解决。

方法# 1

运行这个来创建一个测试:

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

下面你可以看到你的测试文件和测试目录:

$ ls testfile dirtodelete
testfile

dirtodelete:
linktodelete

然后运行common -io deleteDirectory()。它崩溃,说文件没有找到。不知道其他例子是怎么做的。Linux的rm命令会简单地删除链接,目录上的rm -r也会删除链接。

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

方法# 2

运行这个来创建一个测试:

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

下面你可以看到你的测试文件和测试目录:

$ ls dirtodelete testdir
dirtodelete:
dirlinktodelete

testdir:
testfile

然后运行common -io deleteDirectory()或人们发布的示例代码。它不仅会删除目录,还会删除位于被删除目录之外的testfile。(它隐式地取消对目录的引用,并删除内容)。Rm -r只会删除链接。你需要使用类似这样的方法删除被解引用的文件:"find -L dirtodelete -type f -exec rm {} \;"

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

其他回答

Guava提供了一行代码:morefiles . deleterecursive()。

与许多共享的示例不同,它只考虑符号链接,并且不会(默认情况下)删除所提供路径之外的文件。

在Java 7中,我们终于可以通过可靠的符号链接检测来做到这一点。(我不认为Apache的commons-io现在有可靠的符号链接检测,因为它不处理Windows上用mklink创建的链接。)

出于历史原因,这里有一个java 7之前的答案,它遵循符号链接。

void delete(File f) throws IOException {
  if (f.isDirectory()) {
    for (File c : f.listFiles())
      delete(c);
  }
  if (!f.delete())
    throw new FileNotFoundException("Failed to delete file: " + f);
}

在Java 7+中,你可以使用Files类。代码非常简单:

Path directory = Paths.get("/tmp");
Files.walkFileTree(directory, new SimpleFileVisitor<Path>() {
   @Override
   public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
       Files.delete(file);
       return FileVisitResult.CONTINUE;
   }

   @Override
   public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
       Files.delete(dir);
       return FileVisitResult.CONTINUE;
   }
});

一行程序解决方案(Java8),递归删除所有文件和目录,包括起始目录:

try (var dirStream = Files.walk(Paths.get("c:/dir_to_delete/"))) {
    dirStream
        .map(Path::toFile)
        .sorted(Comparator.reverseOrder())
        .forEach(File::delete);
}

我们使用一个颠倒顺序的比较器,否则File::delete将不能删除可能非空的目录。所以,如果你想保留目录,只删除文件,只需在sorted()中删除比较器或完全删除排序,并添加文件过滤器:

try (var dirStream = Files.walk(Paths.get("c:/dir_to_delete/"))) {
    dirStream
        .filter(Files::isRegularFile)
        .map(Path::toFile)
        .forEach(File::delete);
}

也许这个问题的解决方案是使用erickson回答中的代码重新实现File类的delete方法:

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