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

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

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


当前回答

Java 7通过符号链接处理增加了对遍历目录的支持:

import java.nio.file.*;

public static void removeRecursive(Path path) throws IOException
{
    Files.walkFileTree(path, new SimpleFileVisitor<Path>()
    {
        @Override
        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
                throws IOException
        {
            Files.delete(file);
            return FileVisitResult.CONTINUE;
        }

        @Override
        public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException
        {
            // try to delete the file anyway, even if its attributes
            // could not be read, since delete-only access is
            // theoretically possible
            Files.delete(file);
            return FileVisitResult.CONTINUE;
        }

        @Override
        public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException
        {
            if (exc == null)
            {
                Files.delete(dir);
                return FileVisitResult.CONTINUE;
            }
            else
            {
                // directory iteration failed; propagate exception
                throw exc;
            }
        }
    });
}

我使用它作为特定于平台的方法(在这个未测试的代码中):

public static void removeDirectory(Path directory) throws IOException
{
    // does nothing if non-existent
    if (Files.exists(directory))
    {
        try
        {
            // prefer OS-dependent directory removal tool
            if (SystemUtils.IS_OS_WINDOWS)
                Processes.execute("%ComSpec%", "/C", "RD /S /Q \"" + directory + '"');
            else if (SystemUtils.IS_OS_UNIX)
                Processes.execute("/bin/rm", "-rf", directory.toString());
        }
        catch (ProcessExecutionException | InterruptedException e)
        {
            // fallback to internal implementation on error
        }

        if (Files.exists(directory))
            removeRecursive(directory);
    }
}

SystemUtils来自Apache Commons Lang。Processes是私有的,但是它的行为应该是明显的。)

其他回答

一行程序解决方案(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);
}

一个有堆栈且没有递归方法的解决方案:

File dir = new File("/path/to/dir");
File[] currList;
Stack<File> stack = new Stack<File>();
stack.push(dir);
while (! stack.isEmpty()) {
    if (stack.lastElement().isDirectory()) {
        currList = stack.lastElement().listFiles();
        if (currList.length > 0) {
            for (File curr: currList) {
                stack.push(curr);
            }
        } else {
            stack.pop().delete();
        }
    } else {
        stack.pop().delete();
    }
}

虽然可以使用file.delete()轻松删除文件,但要删除的目录必须为空。使用递归可以很容易地做到这一点。例如:

public static void clearFolders(String[] args) {
        for(String st : args){
            File folder = new File(st);
            if (folder.isDirectory()) {
                File[] files = folder.listFiles();
                if(files!=null) { 
                    for(File f: files) {
                        if (f.isDirectory()){
                            clearFolders(new String[]{f.getAbsolutePath()});
                            f.delete();
                        } else {
                            f.delete();
                        }
                    }
                }
            }
        }
    }

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

方法# 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 9之前,Guava一直支持files . deleterecursive (File)。

来自番石榴10:

弃用。该方法存在符号链接检测差和竞态条件差的问题。只有使用操作系统命令(如rm -rf或del /s)才能适当地支持此功能。这个方法计划在Guava 11.0版本中从Guava中移除。

因此,在番石榴11中没有这种方法。