Java : Delete a directory with contents
The Files.delete method can delete an empty directory, but cannot delete a directory with contents. In this article, I will show you how to delete an entire directory with contents.
Note:
- This article may use translation software for your convenience. Please also check the original Japanese version.
Code example
Let's take a look at a code example.
public void deleteAll(Path dir) throws IOException {
Files.walkFileTree(dir, new SimpleFileVisitor<>() {
@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 {
if (exc != null) {
throw exc;
}
Files.delete(dir);
return FileVisitResult.CONTINUE;
}
});
}
The deleteAll method deletes an entire directory with contents.
This code example is based on the FileVisitor usage examples in the official API specification.
Warning
- Be careful not to delete important files and check the operation.
Description
The Files.delete method can delete a file or an empty directory. But cannot delete a directory with contents.
For example, you will prepare the following files and directories. (Commands are executed in PowerShell on Windows)
PS D:\java-work> tree /F
...
D:.
│ aaa.txt
│
├─dir1
└─dir2
bbb.txt
- aaa.txt : file
- dir1 : empty directory
- dir2 : directory with contents (bbb.txt).
You try to delete the files and directories with Files.delete.
final var aaa = Path.of("D:", "java-work", "aaa.txt");
System.out.println(aaa); // D:\java-work\aaa.txt
System.out.println(Files.isRegularFile(aaa)); // true
// Delete OK
Files.delete(aaa);
System.out.println(Files.notExists(aaa)); // true
final var dir1 = Path.of("D:", "java-work", "dir1");
System.out.println(dir1); // D:\java-work\dir1
System.out.println(Files.isDirectory(dir1)); // true
// Delete OK
Files.delete(dir1);
System.out.println(Files.notExists(dir1)); // true
final var dir2 = Path.of("D:", "java-work", "dir2");
System.out.println(dir2); // D:\java-work\dir2
System.out.println(Files.isDirectory(dir2)); // true
try {
// Delete NG
Files.delete(dir2);
} catch (IOException e) {
System.out.println(e);
}
// Result
// ↓
//java.nio.file.DirectoryNotEmptyException: D:\java-work\dir2
PS D:\java-work> tree /F
...
D:.
└─dir2
bbb.txt
The Files.delete method can delete the aaa.txt and the dir1. But cannot delete the dir2, because it is not empty.
To delete the dir2, you must first delete all contents in the dir2.
The Files.walkFileTree method walks through the list of files and directories.
For example, you will prepare the following files and directories.
PS D:\java-work> tree /F
...
D:.
└─target-dir
│ aaa.txt
│ bbb.txt
│
├─dir1
│ │ ccc.txt
│ │ ddd.txt
│ │
│ └─dir1-2
│ eee.txt
│ fff.txt
│
└─dir2
ggg.txt
hhh.txt
You can access all the files and directories in the target-dir with Files.walkFileTree. To access them, use a FileVisitor and receive a callback. This time, we will use a SimpleFileVisitor which is the subclass of the FileVisitor.
final var target = Path.of("D:", "java-work", "target-dir");
System.out.println(target); // D:\java-work\target-dir
System.out.println(Files.isDirectory(target)); // true
Files.walkFileTree(target, new SimpleFileVisitor<>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
System.out.println("visitFile : " + file);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) {
System.out.println("postVisitDirectory : " + dir);
return FileVisitResult.CONTINUE;
}
});
// Result
// ↓
//visitFile : D:\java-work\target-dir\aaa.txt
//visitFile : D:\java-work\target-dir\bbb.txt
//visitFile : D:\java-work\target-dir\dir1\ccc.txt
//visitFile : D:\java-work\target-dir\dir1\ddd.txt
//visitFile : D:\java-work\target-dir\dir1\dir1-2\eee.txt
//visitFile : D:\java-work\target-dir\dir1\dir1-2\fff.txt
//postVisitDirectory : D:\java-work\target-dir\dir1\dir1-2
//postVisitDirectory : D:\java-work\target-dir\dir1
//visitFile : D:\java-work\target-dir\dir2\ggg.txt
//visitFile : D:\java-work\target-dir\dir2\hhh.txt
//postVisitDirectory : D:\java-work\target-dir\dir2
//postVisitDirectory : D:\java-work\target-dir
FileVisitor methods are called back at the following times.
- The visitFile method is called when a file is found.
- The postVisitDirectory method is called after all files in a directory are called on the visitFile method.
In other words, if you delete the file with visitFile, the directory will be empty when postVisitDirectory is called.
Note
- The Files.walkFileTree does not walk symbolic links by default. You use FileVisitOption.FOLLOW_LINKS if you want to access even the destination of symbolic links.
Conclusion
Finally, I wrapped it up as a little utility class.
public class FileUtils {
private FileUtils() {
}
public static void deleteAll(Path dir) throws IOException {
Objects.requireNonNull(dir);
Files.walkFileTree(dir, new SimpleFileVisitor<>() {
@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 {
if (exc != null) {
throw exc;
}
Files.delete(dir);
return FileVisitResult.CONTINUE;
}
});
}
}
You will delete the following target-dir directory with contents.
// --- PowerShell ---
//PS D:\java-work> tree /F
//...
//D:.
//└─target-dir
// │ aaa.txt
// │ bbb.txt
// │
// ├─dir1
// │ │ ccc.txt
// │ │ ddd.txt
// │ │
// │ └─dir1-2
// │ eee.txt
// │ fff.txt
// │
// └─dir2
// ggg.txt
// hhh.txt
final var dir = Path.of("D:", "java-work", "target-dir");
System.out.println(dir); // D:\java-work\target-dir
System.out.println(Files.isDirectory(dir)); // true
FileUtils.deleteAll(dir);
System.out.println(Files.notExists(dir)); // true
The FileUtils.deleteAll method was able to delete the target-dir directory with contents.