2

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.

Community
  • 1
  • 1
Silird
  • 176
  • 1
  • 11
  • 1
    Lots of things are called "explorer." Do you mean Windows Explorer? – T.J. Crowder Oct 18 '19 at 16:12
  • Have you tried FileUtils.forceDelete(directory)? – Yasham Oct 18 '19 at 16:13
  • @T.J.Crowder, Yes, the problem appeared on windows 10 in Windows Explorer, but it's also appeare during normal execution of the program when Windows Explorer was closed. – Silird Oct 18 '19 at 16:16
  • Use org.apache.commons.io and let me know if it works – Yasham Oct 18 '19 at 16:18
  • @Yasham, just tried and the result is the same as when runing FileUtils.deleteDirectory – Silird Oct 18 '19 at 16:20
  • If there's any process acessing a folder or an item in that folder you will not be able to delete the file/folder, windows will not allow it, it's not a java problem. – Cλstor Oct 18 '19 at 16:28
  • @Cλstor, but it normally delete folder in debug mode step by step, windows explorer just try to undo or show error message if it's not possible. – Silird Oct 18 '19 at 16:32
  • Try this once FileDeleteStrategy.FORCE.delete(aFile); – Yasham Oct 18 '19 at 16:53
  • @Yasham, this one doesn't work too – Silird Oct 18 '19 at 17:01
  • read this https://stackoverflow.com/questions/40706380/failed-to-delete-a-file-in-windows-using-java – Yasham Oct 18 '19 at 17:26
  • @Yasham, in my case, if I delete a folder using only java.nio, then I don't get an error "The process cannot access the file because it is being used by another process" like in that question, only "java.nio.file.DirectoryNotEmptyException". As if Java does not keep up with its own operations and the information on the state of the folder is not relevant, possibly due to some kind of caching? – Silird Oct 18 '19 at 17:50
  • @Silird - _"Java does not keep up with its own operations"_ — _Java_ is not deleting the files, it is _asking_ the Operating System to delete the files, and different OSes behave differently. The OS _could_ return immediately before the delete operation is complete. Unix behaves differently when deleting open files than Windows does. Looking at this problem as if Java is not "keeping up" with itself is unlikely to help you. Perhaps if you look at it like waiting for an async operation to complete would be more helpful. – Stephen P Oct 18 '19 at 19:01

0 Answers0