1. Introduction

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.

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 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.

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 for actions to be performed.

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:

  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 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.

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介绍