Search code examples
javapathclassloader

ClassLoader.getResource returns odd path (maybe)?


When loading an asset such as a text file from the resources folder, the most common approach is to use ClassLoader to get the path:

String path = getClass().getClassLoader().getResource("file.txt").getPath();

You can then use any of the many readers that java has to read the content of that file. But for some reason, Paths.get(path) is not happy with the path:

byte[] content = Files.readAllBytes(Paths.get(path)) 
    -> throws java.nio.file.InvalidPathException when executed

ClassLoader.getResource(...).getPath() is returning:

/D:/Projects/myapp/build/resources/main/file.txt

Paths.get() doesn't like it. Apparently the ':' after /D is an 'Illegal char'. (Note that the path seems correct, the file is actually there)

Which one is causing the problem? Is ClassLoader.getResource() returning an invalid path or is Paths.get() acting up over nothing?


Some time later
It seems that there are multiple different formats for paths in java. The various frameworks don't appear to completely agree on what is right and what is wrong, therefore there are various discrepancies between the paths that they create and accept.

In this example, Paths.get() was in fact not expecting the leading slash in the path:

/D:/Projects/myapp/build/resources/main/vertex.vs.glsl <- EVIL
D:/Projects/myapp/build/resources/main/vertex.vs.glsl <- OK

I suppose that the question now is: How do I sanitise file paths returned by ClassLoader.getResource() for use with Paths.get() properly? Are there any other differences between their two file path formats?


Solution

    1. "the most common approach" is not necessarily the best :)
    2. Take care which path you mean: ClassLoader.getResource() returns a URL, which can have a path component. However, this is not necessarily a valid file-path.
      Note, that there is also a method Paths.get(URI) which takes a URI as parameter
    3. The first slash in /D:/Projects/myapp/build/resources/main/file.txt just means, that this is an absolute path: see Class.getResource
    4. I recommend, that you simply use ClassLoader.html#getResourceAsStream when you want to read a file

    Update to answer comment: "So why does Paths.get() not accept the absolute path?"

    Paths.get() does accept absolute paths.
    But you must pass a valid (file-)path - and in your case you pass the URL-path directly (which is not a valid file-path).

    • When you call: getClass().getClassLoader().getResource("file.txt") it returns a URL: file:/D:/Projects/myapp/build/resources/main/file.txt
      • this URL consists of the schema file:
      • and the valid (absolute URL)path: /D:/Projects/myapp/build/resources/main/file.txt
    • you try to use this URL-path directly as a file-path, which is wrong

    To convert the URL path to a valid file-path you could use the Paths.get(URI) method like so:

    URL fileUrl = getClass().getClassLoader().getResource("file.txt");
    Path filePath = Paths.get(fileUrl.toURI());
    // now you have a valid file-path: D:/Projects/myapp/build/resources/main/file.txt