1. Introduction

In this tutorial, 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.

2. Deleting a Directory Recursively

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:

  1. Get all the contents of the directory for deletion
  2. Delete all children that are not a directory (exit from recursion)
  3. For each subdirectory of the 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 the pathToBeDeleted location and the @After method cleans up the directory if required.

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.

3. Using FileUtils from commons-io

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

<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.15.1</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(file);

4. Using FileSystemUtils from Spring

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

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

The latest version of the dependency can be found here.

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

boolean result = FileSystemUtils.deleteRecursively(file);

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

5. Using NIO2 With Java 7

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

public void givenDirectory_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’ll define SimpleFileVisitor to take the following actions for the generated events:

  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.

6. Using NIO2 With Java 8

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

@Test
public void givenDirectory_whenDeletedWithFilesWalk_thenIsGone() throws IOException {
    Path pathToBeDeleted = TEMP_DIRECTORY.resolve(DIRECTORY_NAME);
    try (Stream paths = Files.walk(pathToBeDeleted)) {
        paths.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 the directories themselves. After that it maps Path to File and deletes each File.

7. Conclusion

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.

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


« 上一篇: Spring框架与Yarg集成
» 下一篇: Vavr验证API介绍