The java.nio.file.Files
API is a really nice improvement over the old java.io.File
class, but one detail strikes me as odd; with the exception of delete()
no methods document that they may throw NoSuchFileException
, and even delete()
says this is optional.
I'd like to be able to differentiate between failures due to missing files and other IO issues, but it seems this isn't guaranteed to be possible.
The alternative of calling Files.exists()
and the like beforehand risks a race-condition if the file is created in between the two operations.
Can I expect methods in Files
will raise a NoSuchFileException
when appropriate? If so, where is this documented? If not, how can I safely determine the failure is due to a missing file?
Example: On Windows 7 with Java 7.0.02 the Files.readAllLines()
method does raise a NoSuchFileException
, though it's not explicitly documented to do so:
Files.readAllLines(Paths.get("foo"), StandardCharsets.UTF_8)
java.nio.file.NoSuchFileException: foo at sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:79) at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:97) at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:102) at sun.nio.fs.WindowsFileSystemProvider.newByteChannel(WindowsFileSystemProvider.java:229) at java.nio.file.Files.newByteChannel(Files.java:315) at java.nio.file.Files.newByteChannel(Files.java:361) at java.nio.file.spi.FileSystemProvider.newInputStream(FileSystemProvider.java:380) at java.nio.file.Files.newInputStream(Files.java:106) at java.nio.file.Files.newBufferedReader(Files.java:2660) at java.nio.file.Files.readAllLines(Files.java:2993)
You can use a full path spec instead of just a filename, or put the filename in a special "Resources" directory and reference it using a relative path, or move the file to wherever your default directory is. Save this answer.
In general: no, you cannot trust methods in java.nio.file.Files
to throw a NoSuchFileException
when expected, but you can verify.
As you can see from the stacktrace, Files
uses the FileSystemProvider
to perform the file operations. The FileSystemProvider
implementations are restricted (like the WindowsFileSystemProvider
) and in turn use a lot of native (C) code. For example, I traced the NoSuchFileException
to the WindowsException which relies on the operating system to report a ERROR_FILE_NOT_FOUND
or ERROR_PATH_NOT_FOUND
. Another example is the newInputStream
route which goes from ChannelInputStream to WindowsChannelFactory to WindowsNativeDispatcher.c which finally calls the native Windows function CreateFileW.
Given the amount of code involved just for Windows, I do not see how this can be trusted to work the same for for example Linux which uses the code here and here.
In my experience, Linux (Posix) file system behavior is pretty consistent, but the Windows (NT) file system behavior is not: I would not assume Windows 7 to behave exactly the same as Windows 8.
Finally, a remark about the race condition: no file system I know guarantees that files listed in a directory actually exist (some files may have already been deleted, especially when using multiple threads operating on files in the same directory). Using methods like Files.exists()
beforehand is, in my experience, a bad idea unless you are about to allocate a lot of resources (e.g. creating a connection to upload a file). When dealing with files, it is better to assume everything is in order and catch exceptions and then try to determine what is wrong. E.g. when reading a file, open it without checking if the file exists, and if you catch an error, check if the file existed to start with. This can save a lot of I/O operations which in turn will help performance.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With