I'm trying to find a way to delete a non-empty directory. But all the solutions I found had a common issue - they do not completely delete a non-empty folder under certain circumstances such as if it's open in explorer.
This problem appears not only when it opens in explorer, but also in other cases, which I have not been able to identify yet. But point is that deletion doesnt always work correctly. It seems that Java does not have time to update the directory information and thinks that the folder is not empty, although all the files in it have already been deleted, because if execute the code in debug mode or make the thread fall asleep for at least 1 ms after each deletion, the folder always removes correctly.
Here are some solutions I found:
// Using java.io.File
public static boolean deleteDirectory(File path) {
if (path.exists()) {
File[] files = path.listFiles();
if (files != null) {
for (File file : files) {
deleteDirectory(file);
}
}
Path p = path.toPath();
try {
Files.delete(p);
return true;
}
catch (IOException ex) {
ex.printStackTrace();
return false;
}
}
return true;
}
// Using java.nio.file.*
public static boolean deleteDirectory(Path path) {
try {
Files.walk(path)
.sorted(Comparator.reverseOrder())
.map(Path::toFile)
.forEach(file -> {
if (!file.delete()) throw new DeleteIOException(file);
});
}
catch (IOException ex) {
log.error("Ошибка удаления директории: " + path.toAbsolutePath(), ex);
return false;
}
catch (DeleteIOException ex) {
return false;
}
return true;
}
// Using NIO2 Java 7
public static boolean deleteDirectoryWalkTree(Path path) {
FileVisitor<Path> visitor = 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 {
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;
}
};
try {
Files.walkFileTree(path, visitor);
}
catch (IOException e) {
e.printStackTrace();
return false;
}
return true;
}
// Using FileUtils from commons-io
public static boolean deleteDirectoryCommonsIO(File path) {
try {
FileUtils.deleteDirectory(path);
}
catch (IOException e) {
e.printStackTrace();
return false;
}
return true;
}
// Using FileSystemUtils from Spring
public static boolean deleteDirectorySpring(Path path) {
try {
return FileSystemUtils.deleteRecursively(path);
}
catch (IOException e) {
e.printStackTrace();
return false;
}
}
I've used this code to test and reproduce issue. I've set the breakpoint before the deletion and opened in explorer "BASE_TEST_DIRECTORY/1/s1". All tests failed.
@BeforeEach
private void createAndTestDirectories() throws IOException {
String dir1 = "1";
String dir2 = "2";
String dir3 = "3";
String subDir1 = "s1";
String subDir2 = "s2";
String subDir3 = "s3";
String fileName = "file";
createDirectory(Paths.get(BASE_TEST_DIRECTORY));
createDirectory(Paths.get(BASE_TEST_DIRECTORY, dir1));
createDirectory(Paths.get(BASE_TEST_DIRECTORY, dir2));
createDirectory(Paths.get(BASE_TEST_DIRECTORY, dir3));
createDirectory(Paths.get(BASE_TEST_DIRECTORY, dir1, subDir1));
createDirectory(Paths.get(BASE_TEST_DIRECTORY, dir1, subDir2));
createDirectory(Paths.get(BASE_TEST_DIRECTORY, dir1, subDir3));
createFile(Paths.get(BASE_TEST_DIRECTORY, dir1, subDir1, fileName));
assertTrue(Files.exists(Paths.get(BASE_TEST_DIRECTORY)));
assertTrue(Files.exists(Paths.get(BASE_TEST_DIRECTORY, dir1, subDir1, fileName)));
}
@Test
public void deleteDirectoryPath_ExpectCleanDirectory() {
boolean result = DeleteDirectoryUtil.deleteDirectory(Paths.get(BASE_TEST_DIRECTORY));
assertTrue(result);
assertFalse(Files.exists(Paths.get(BASE_TEST_DIRECTORY)));
}
I've get errors such:
java.nio.file.DirectoryNotEmptyException: target\ioTests\1
...
java.io.IOException: Unable to delete directory target\ioTests\1.
...
Any suggestions to resolve it and don't use Thread.sleep(1)?
UPD
The problem appears on Windows 10. I didn't testing on other os.
The problem appears not only if the folder is opened in Explorer, the described case with Explorer is only the easiest way to reproduce it.
Removal sometimes does not work completely during normal program execution when no processes occur in the folder, which is why I asked this question. Sometimes it's completely removed, and sometimes not.
This problem is not only due to Windows Explorer, because when you delete the folder in which it is located, it's deleted successfully and Explorer tries to display the previous folder as if you pressed alt + left arrow or backspace. And also, as I described above, deletion works fine even when folder opened in explorer if you execute the code in debug mode step by step or if you insert a this code after each deletion:
try {
Thread.sleep(1);
}
catch (InterruptedException e) {
e.printStackTrace();
}
Other solutions from org.apache.commons.io (FileUtils.forceDelete(directory) and FileDeleteStrategy.FORCE.delete(aFile)) that @Yasham proposed in comments have the same issue.