在Java中递归删除目录

paw*_*que 369 java filesystems file-io delete-directory

有没有办法在Java中递归删除整个目录?

在正常情况下,可以删除空目录.但是,当要删除包含内容的整个目录时,它就不再那么简单了.

如何用Java中的内容删除整个目录?

Ste*_*e K 454

你应该查看Apache的commons-io.它有一个FileUtils类,可以做你想要的.

FileUtils.deleteDirectory(new File("directory"));
Run Code Online (Sandbox Code Playgroud)

  • 它更彻底一点.它在Linux/Unix上正确处理符号链接等内容.http://svn.apache.org/viewvc/commons/proper/io/trunk/src/java/org/apache/commons/io/FileUtils.java?view=markup (14认同)
  • 这个函数可能包含了erickson在他的答案中提供的代码. (3认同)
  • 当 Java 具有开箱即用的功能时,为什么还要添加另一个依赖项呢?请参阅本页 RoK 的回答,或 /sf/ask/2519173471/ (2认同)

eri*_*son 189

使用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);
}
Run Code Online (Sandbox Code Playgroud)

  • 要非常小心*.这将取消引用符号链接.如果你在linux上,并且有一个带有链接`foo/link`的文件夹`foo`,那么`link - > /`,调用`delete(new File(foo))`*会删除你的文件系统因为你的用户被允许!!* (44认同)
  • @Erickson:对于删除失败,FileNotFoundException不是一个很糟糕的例外吗?如果文件真的不再存在,那么它必须已被删除,这意味着,从语义上讲,删除没有失败 - 它没有任何关系.如果由于其他原因导致失败,那不是因为找不到文件. (14认同)
  • @ Joehot200你是对的,在一个目录上调用delete符号链接不会删除目录,只是符号链接本身.删除目录实际上需要使用[ReadSymbolicLink]显式地遵循符号链接(https://docs.oracle.com/javase/8/docs/api/java/nio/file/Files.html#readSymbolicLink-java.nio.file .路径-).我的错!好眼力 (12认同)
  • File.delete()没有该功能. (11认同)
  • @Miquel这没有意义 - 我们为什么要小心?当然,提供的代码的要点是删除整个目录,这就是它的目的.我不明白这里有什么危险. (4认同)
  • 在递归删除期间跟随符号链接是“非常危险的”。我丢失了一个不可替代的照片和视频集合,因为在递归删除期间,gnome-vfs的第一个版本意外将以下符号链接设置为默认行为。这不是rm的默认行为,这有充分的理由! (2认同)

Tom*_*ski 144

在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;
   }
});
Run Code Online (Sandbox Code Playgroud)

  • "代码是"非"非常简单"简单地删除目录:-)但是嘿,这是我认为纯java中的最佳解决方案. (17认同)
  • 你可能应该在你的 `postVisitDirectory` 方法中调用 `super.postVisitDirectory(dir, exc);`,如果 walk 无法列出目录,就会爆炸。 (3认同)
  • 这个解决方案看起来非常优雅,根本不包含目录遍历逻辑! (2认同)

Tre*_*son 67

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;
            }
        }
    });
}
Run Code Online (Sandbox Code Playgroud)

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

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

(SystemUtils来自Apache Commons Lang.进程是私有的,但其行为应该是显而易见的.)


RoK*_*RoK 62

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

Files.walk(Paths.get("c:/dir_to_delete/"))
                .map(Path::toFile)
                .sorted((o1, o2) -> -o1.compareTo(o2))
                .forEach(File::delete);
Run Code Online (Sandbox Code Playgroud)

我们使用比较器反转顺序,否则File :: delete将无法删除可能的非空目录.因此,如果您想保留目录并且只删除文件,只需删除sorted()中的比较器完全删除排序并添加文件过滤器:

Files.walk(Paths.get("c:/dir_to_delete/"))
                .filter(Files::isRegularFile)
                .map(Path::toFile)
                .forEach(File::delete);
Run Code Online (Sandbox Code Playgroud)

  • 这个答案有一个问题:“walk()”返回的流应该被关闭,因为它“包含对一个或多个打开目录的引用”(Javadoc)。 (5认同)
  • 正确的方法是 `.sorted(Comparator.reverseOrder())` 建议 `Comparator::reverseOrder` **不** 工作。参见:/sf/ask/3012562801/ (3认同)
  • Robin,请注意“ -o1.compareTo(o2)”中的减号。与.sorted(Comparator.reverseOrder)相同 (2认同)

Pau*_*tex 32

刚刚看到我的解决方案与erickson的解决方案大致相同,只是打包为静态方法.把它放在某个地方,它比安装所有Apache Commons的重量要轻得多(如你所见)非常简单.

public class FileUtils {
    /**
     * By default File#delete fails for non-empty directories, it works like "rm". 
     * We need something a little more brutual - this does the equivalent of "rm -r"
     * @param path Root File Path
     * @return true iff the file and all sub files/directories have been removed
     * @throws FileNotFoundException
     */
    public static boolean deleteRecursive(File path) throws FileNotFoundException{
        if (!path.exists()) throw new FileNotFoundException(path.getAbsolutePath());
        boolean ret = true;
        if (path.isDirectory()){
            for (File f : path.listFiles()){
                ret = ret && deleteRecursive(f);
            }
        }
        return ret && path.delete();
    }
}
Run Code Online (Sandbox Code Playgroud)


小智 19

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

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 看到你通常没有问题嵌套几百个方法调用我认为你很可能早就遇到文件系统限制. (4认同)
  • +1使用堆栈.这将适用于包含深层次嵌套子目录的目录,而其他基于堆栈的方法将失败. (2认同)
  • 小心`java.io.File`类的`list*`方法.来自Javadocs:"如果此抽象路径名不表示目录,或者发生I/O错误,则返回null." 所以:`if(currList.length> 0){`变成`if(null!= currList && currList.length> 0){` (2认同)

And*_*lay 15

番石榴一直Files.deleteRecursively(File)支持到番石榴9.

来自番石榴10:

已过时.该方法存在差的符号链检测和竞争条件.仅通过外壳到诸如rm -rf或的操作系统命令,可以适当地支持此功能del /s.计划在Guava版本11.0中从Guava中删除此方法.

因此,番石榴11中没有这样的方法.

  • 太糟糕了.脱壳似乎有点粗糙,不便携.如果Apache commons版本正常工作,那么可能实现它并非不可能. (6认同)
  • @andrew Apache Commons实现应该与导致Guava删除其实现的问题类似,请参阅http://code.google.com/p/guava-libraries/issues/detail?id=365 (6认同)
  • Guava 21.0将此添加为[MoreFiles.deleteRecursively()](https://google.github.io/guava/releases/21.0/api/docs/com/google/common/io/MoreFiles.html#deleteRecursively-java.nio .file.Path-com.google.common.io.RecursiveDeleteOption ...-). (3认同)

Ben*_*son 14

如果你有Spring,你可以使用FileSystemUtils.deleteRecursively:

import org.springframework.util.FileSystemUtils;

boolean success = FileSystemUtils.deleteRecursively(new File("directory"));
Run Code Online (Sandbox Code Playgroud)


use*_*782 12

for(Path p : Files.walk(directoryToDelete).
        sorted((a, b) -> b.compareTo(a)). // reverse; files before dirs
        toArray(Path[]::new))
{
    Files.delete(p);
}
Run Code Online (Sandbox Code Playgroud)

或者如果你想处理IOException:

Files.walk(directoryToDelete).
    sorted((a, b) -> b.compareTo(a)). // reverse; files before dirs
    forEach(p -> {
        try { Files.delete(p); }
        catch(IOException e) { /* ... */ }
      });
Run Code Online (Sandbox Code Playgroud)

  • 这有助于我提出一个Scala版本:`Files.walk(path).iterator().toSeq.reverse.foreach(Files.delete)` (2认同)

Ada*_*ler 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();
}
Run Code Online (Sandbox Code Playgroud)

  • 具有布尔返回值且无重复的增强版本:http://pastebin.com/PqJyzQUx (5认同)

小智 8

static public 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();
    }
}
Run Code Online (Sandbox Code Playgroud)


小智 5

符号链接和上面的代码失败的两种方法...不知道解决方案。

方式1

运行此命令以创建测试:

echo test > testfile
mkdir dirtodelete
ln -s badlink dirtodelete/badlinktodelete
Run Code Online (Sandbox Code Playgroud)

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

$ ls testfile dirtodelete
testfile

dirtodelete:
linktodelete
Run Code Online (Sandbox Code Playgroud)

然后运行commons-io deleteDirectory()。崩溃提示找不到文件。不知道其他示例在这里做什么。Linux rm命令将只删除链接,目录中的rm -r也将删除。

Exception in thread "main" java.io.FileNotFoundException: File does not exist: /tmp/dirtodelete/linktodelete
Run Code Online (Sandbox Code Playgroud)

方式#2

运行此命令以创建测试:

mkdir testdir
echo test > testdir/testfile
mkdir dirtodelete
ln -s ../testdir dirtodelete/dirlinktodelete
Run Code Online (Sandbox Code Playgroud)

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

$ ls dirtodelete testdir
dirtodelete:
dirlinktodelete

testdir:
testfile
Run Code Online (Sandbox Code Playgroud)

然后运行commons-io deleteDirectory()或其他人发布的示例代码。它不仅删除目录,还删除正在删除的目录之外的测试文件。(它隐式地取消引用目录,并删除内容)。rm -r仅删除链接。您需要使用类似以下的方法删除已取消引用的文件:“查找-Ldirodelete -t​​ype f -exec rm {} \;”。

$ ls dirtodelete testdir
ls: cannot access dirtodelete: No such file or directory
testdir:
Run Code Online (Sandbox Code Playgroud)