Delete a Directory Recursively in Java – 在Java中递归地删除一个目录

最后修改: 2017年 9月 3日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.介绍

In this article, we’ll illustrate how to delete a directory recursively in plain Java. We’ll also look at some alternatives for deleting directories using external libraries.

在这篇文章中,我们将说明如何在普通Java中递归地删除一个目录。我们还将看一下使用外部库删除目录的一些替代方法。

2. Deleting a Directory Recursively

2.递归删除一个目录

Java has an option to delete a directory. However, this requires the directory to be empty. So, we need to use recursion to delete a particular non-empty directory:

Java有一个选项可以删除一个目录。然而,这需要该目录是空的。因此,我们需要使用递归来删除一个特定的非空目录。

  1. Get all the contents of the directory to be deleted
  2. Delete all children that are not a directory (exit from recursion)
  3. For each subdirectory of current directory, start with step 1 (recursive step)
  4. Delete the directory

Let’s implement this simple algorithm:

让我们来实现这个简单的算法。

boolean deleteDirectory(File directoryToBeDeleted) {
    File[] allContents = directoryToBeDeleted.listFiles();
    if (allContents != null) {
        for (File file : allContents) {
            deleteDirectory(file);
        }
    }
    return directoryToBeDeleted.delete();
}

This method can be tested using a straightforward test case:

这种方法可以用一个直接的测试案例来测试。

@Test
public void givenDirectory_whenDeletedWithRecursion_thenIsGone() 
  throws IOException {
 
    Path pathToBeDeleted = TEMP_DIRECTORY.resolve(DIRECTORY_NAME);

    boolean result = deleteDirectory(pathToBeDeleted.toFile());

    assertTrue(result);
    assertFalse(
      "Directory still exists", 
      Files.exists(pathToBeDeleted));
}

The @Before method of our test class creates a directory tree with subdirectories and files at pathToBeDeleted location and @After method cleans up directory if required.

我们测试类的@Before方法在pathToBeDeleted位置创建一个带有子目录和文件的目录树,@After方法在需要时清理了目录。

Next, let’s have a look at how we can achieve deletion using two of the most commonly used libraries – Apache’s commons-io and Spring Framework’s spring-core. Both of these libraries allow us to delete the directories using just a single line of code.

接下来,让我们看看如何使用两个最常用的库–Apache的commons-io和Spring框架的spring-core.来实现删除。这两个库都允许我们只用一行代码来删除目录。

3. Using FileUtils from commons-io

3.使用来自commons-ioFileUtils

First, we need to add the commons-io dependency to the Maven project:

首先,我们需要向Maven项目添加commons-io依赖项。

<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.11.0</version>
</dependency>

The latest version of the dependency can be found here.

最新版本的依赖性可以在这里找到。

Now, we can use FileUtils to perform any file-based operations including deleteDirectory() with just one statement:

现在,我们可以使用FileUtils来执行任何基于文件的操作,包括deleteDirectory(),只需一条语句。

FileUtils.deleteDirectory(file);

4. Using FileSystemUtils from Spring

4.从Spring中使用FileSystemUtils

Alternatively, we can add the spring-core dependency to the Maven project:

另外,我们可以在Maven项目中添加spring-core依赖项。

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>4.3.10.RELEASE</version>
</dependency>

The latest version of the dependency can be found here.

最新版本的依赖性可以在这里找到。

We can use the deleteRecursively() method in FileSystemUtils to perform the deletion:

我们可以使用deleteRecursively()中的FileSystemUtils方法来执行删除。

boolean result = FileSystemUtils.deleteRecursively(file);

The recent releases of Java offer newer ways of performing such IO operations described in the following sections.

最近发布的Java提供了较新的执行此类IO操作的方法,在下面的章节中有所描述。

5. Using NIO2 With Java 7

5.使用Java 7的NIO2

Java 7 introduced a whole new way of performing file operations using Files. It allows us to traverse a directory tree and use callbacks for actions to be performed.

Java 7引入了一种使用Files执行文件操作的全新方式。它允许我们遍历一个目录树,并使用回调来执行操作。

public void whenDeletedWithNIO2WalkFileTree_thenIsGone() 
  throws IOException {
 
    Path pathToBeDeleted = TEMP_DIRECTORY.resolve(DIRECTORY_NAME);

    Files.walkFileTree(pathToBeDeleted, 
      new SimpleFileVisitor<Path>() {
        @Override
        public FileVisitResult postVisitDirectory(
          Path dir, IOException exc) throws IOException {
            Files.delete(dir);
            return FileVisitResult.CONTINUE;
        }
        
        @Override
        public FileVisitResult visitFile(
          Path file, BasicFileAttributes attrs) 
          throws IOException {
            Files.delete(file);
            return FileVisitResult.CONTINUE;
        }
    });

    assertFalse("Directory still exists", 
      Files.exists(pathToBeDeleted));
}

The Files.walkFileTree() method traverses a file tree and emits events. We need to specify callbacks for these events. So, in this case, we will define SimpleFileVisitor to take the following actions for the generated events:

Files.walkFileTree()方法遍历了一个文件树并发出了事件。我们需要为这些事件指定回调。因此,在这种情况下,我们将定义SimpleFileVisitor,为生成的事件采取以下行动。

  1. Visiting a file – delete it
  2. Visiting a directory before processing its entries – do nothing
  3. Visiting a directory after processing its entries- delete the directory, as all entries within this directory would have been processed (or deleted) by now
  4. Unable to visit a file – rethrow IOException that caused the failure

Please refer to Introduction to the Java NIO2 File API for more details on NIO2 APIs on handling file operations.

请参考Java NIO2文件API简介,以了解关于处理文件操作的NIO2 API的更多细节。

6. Using NIO2 With Java 8

6.使用NIO2与Java 8

Since Java 8, Stream API offers an even better way of deleting a directory:

从Java 8开始,Stream API提供了一种更好的删除目录的方法。

@Test
public void whenDeletedWithFilesWalk_thenIsGone() 
  throws IOException {
    Path pathToBeDeleted = TEMP_DIRECTORY.resolve(DIRECTORY_NAME);

    Files.walk(pathToBeDeleted)
      .sorted(Comparator.reverseOrder())
      .map(Path::toFile)
      .forEach(File::delete);

    assertFalse("Directory still exists", 
      Files.exists(pathToBeDeleted));
}

Here, Files.walk() returns a Stream of Path that we sort in reverse order. This places the paths denoting the contents of directories before directories itself. Thereafter it maps Path to File and deletes each File.

在这里,Files.walk()返回一个StreamPath,我们以相反的顺序排序。这就把表示目录内容的路径放在了directories本身之前。此后,它将Path映射到File并删除每个File。文件

7. Conclusion

7.结语

In this quick tutorial, we explored different ways of deleting a directory. While we saw how to use recursion to delete, we also looked at some libraries, NIO2 leveraging events and Java 8 Path Stream employing a functional programming paradigm.

在这个快速教程中,我们探索了删除一个目录的不同方法。当我们看到如何使用递归来删除时,我们也看了一些库,NIO2利用事件和Java 8 Path Stream采用了函数式编程范式。

All source code and test cases for this article are available over on GitHub.

本文的所有源代码和测试案例都可以在GitHub上找到