Java | Attempting to delete files on a Windows machine results in "phantom files"
Asked Answered
M

2

7

I'm trying to delete some files on a windows machine using apache commons-io's FileUtils.deleteDirectory method (The version of the commons library is 2.4). Said method eventually calls the "forceDelete" FileUtils method which calls "file.delete()" on line 2273 here:

2268  public static void forceDelete(File file) throws IOException {
2269      if (file.isDirectory()) {
2270          deleteDirectory(file);
2271      } else {
2272          boolean filePresent = file.exists();
2273          if (!file.delete()) {
2274              if (!filePresent){
2275                  throw new FileNotFoundException("File does not exist: " + file);
2276              }
2277             String message =
2278                   "Unable to delete file: " + file;
2279                throw new IOException(message);
2280        }
2281     }
2282 }

Now, file.delete, calls FileSystem.delete(this) for the file in question and this is the part where I'm not exactly sure what happens. FileSystem.delete is supposed to return true if the file is deleted and false if there was some sort of issue and the file was not deleted. When trying to delete the file the method returns "true" but the file remains in the directory and because of the directory not being emptied properly it cannot be deleted later in the FileUtils.deleteDirectory method throwing an exception.

The file can be deleted manually, from the windows file explorer at any time until the fileSystem.delete(this) method is called. Once that method is called the file cannot be deleted by the file explorer and disappears when the application is closed.

The reason I mentioned windows several times is because this issue does not occur on linux machines.

Which finally leads me to my question, "Does anyone know what might be the cause of this issue or at least give some insight in how to get some answers?"

This is my first post so I'm sorry if I did something wrong, thanks in advance.

Merete answered 30/3, 2022 at 12:23 Comment(1)
I wish all first questions by new users would come with such quality.Thirtyeight
L
2

java.io.File is a very old class. It was part of Java’s very first release in the 1990s. Object-oriented design wasn’t as well understood back then, and the File class has a number of flaws, both in the API design and in the implementation.

Its modern replacement is the java.nio.file package, in particular the Path class for representing filenames, and the Files class (note the ‘s’) for performing file operations.

The same functionality, using java.nio.file, might look like this:

public static void forceDelete(File file) throws IOException {
    Files.walkFileTree(file.toPath(), new SimpleFileVisitor<Path>() {
        @Override
        public FileVisitResult visitFile(Path file,
                                         BasicFileAttributes attrs)
        throws IOException {
            Files.delete(file);
            return super.visitFile(file, attrs);
        }

        @Override
        public FileVisitResult postVisitDirectory(Path dir,
                                                  IOException e)
        throws IOException {
            Files.delete(dir);
            return super.postVisitDirectory(dir, e);
        }
    });
}

The old File.delete() doesn’t throw an exception; it just returns a boolean, and you have no way to know why the operation failed. The java.nio.file package fixes this; you will get an informative exception if an operation fails. So, the above implementation may not fix the problem, but at least you will have a good idea of what happened.

Labbe answered 30/3, 2022 at 13:37 Comment(0)
C
2

The behavior is by design of the Windows OS NTFS subsystem: You might delete a file sucessfully but it stays visible within the containing directory as long as the file is opened by some (other) process. That means: only when the last Handle to the file is being closed the file will vanish from the filesystem.

Brand-new Windows Internals, Part 2, 7th Edition book covers this topic in "Chapter 11 --> The NT File System --> POSIX-style delete semantics" on pages 641, 642 in detail.

I guess your Java application has opened the file for read or write without properly closing it before you successfully call File.delete(). Or it might be the Java File class object itself, which holds a handle open -- I'm not a Java expert. In any case the file will stay until your JVM terminates or the File object gets garbage collected.

Give it a try with Windows 10, May 2019 Update (19H1) or later: Microsoft changed the DeleteFile() API default semantics to POSIX style and your problem should be solved by that ;)

Or use Files.delete(file) as VGR pointed out in his/her previous answer.

Countermeasure answered 21/4, 2022 at 19:20 Comment(1)
See also Win32 API documentation for DeleteFile(): "The DeleteFile function marks a file for deletion on close. Therefore, the file deletion does not occur until the last handle to the file is closed"Countermeasure

© 2022 - 2024 — McMap. All rights reserved.