ClassLoader.getResource returns odd path (maybe)? - java

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?

"the most common approach" is not necessarily the best :)
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
The first slash in /D:/Projects/myapp/build/resources/main/file.txt just means, that this is an absolute path: see Class.getResource
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
thus the Paths.get(String,..) method throws an InvalidPathException
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

Please, have a look at the result of getClass().getClassLoader().getResource("file.txt"). It's a URL. With getPath() then you just retrieve the path part of that URL, ignoring protocol and server part. Opening the path part as a file might work under certain circumstances (in the easy cases where file syntax and URL path syntax match), but don't do it in production code.
Why? When you leave your IDE and deliver your application as JAR or WAR, the resources will reside inside a ZIP-compressed file, and there will be no file "file.txt" that you can open, there's only an entry in a JAR or WAR file.
As #TmTron pointed out, I also recommend to use ClassLoader.getResourceAsStream(). That will work in all cases.

Related

How to convert network path to URL in Java

I have literally searched the whole internet for this question but I have not found an answer. I have a file, in the network and I want to create an Itext image with it and for that, I have to convert its path to URL. The problem is when I use path.toURI().toURL() it appends my project path to the URL such that my URL ends up starting with C:/ which will not work.
Is there a way to just convert a string to file URL in java?
I have tried this:
String paths = "‪\\\\DESKTOP-A11F076\\Users\\Benson Korir\\Desktop\\walgotech\\passport.jpg";
String first = "file:" + paths.replaceAll("\\\\", "//").replaceAll("////", "//");
String second = "file://desktop-a11f076//Users//Benson Korir//Desktop//walgotech//passport.jpg";
System.out.println(first);
System.out.println(second);
The second string I have copied directly from the browser and it works fine. Funny this is these two strings output the same thing but the first string brings an error when I use it here:
Image image1 = Image.getInstance(second);
I am getting the error below:
java.io.FileNotFoundException: ‪\DESKTOP-A11F076\Users\Benson Korir\Desktop\walgotech\passport.jpg (The system cannot find the path specified)
If I got your requirement correctly, your path is a UNC file name, and that is the short form of an SMB path, with DESKTOP-A11F076 being the remote machine, and \Users\Benson Korir\Desktop\walgotech\passport.jpg being the path to the file on that machine.
If I am correct with that assumption, my understanding is that your URL have to look like this: smb://‪DESKTOP-A11F076/Users/Benson Korir/Desktop/walgotech/passport.jpg.
As far I remember is a Java java.io.File object capable to handle a UNC file name (this article implies that, too), but when translating it to a URI, it tries to make it absolute first, and there it fails in your case.
I usually avoid working on Windows whenever possible, therefore I have no environment to verify that.

Why can I get a valid url from getClass().getResource(), but the url that is returned creates a file that doesn't exist

I am trying to load some data into an AWS lambda and am using getClass().getResource() to do so. This returns a nice URL that in logs seemingly prints out a plausible url; however, when I try and make a file based on that path, I get a file that when I call .exists() returns false.
If I run the code bellow, the first print statement gives "returns exists: false"
Meanwhile, the second print statement gives something around the lines of "test path: /file:/var/task/lib/MyLambda-1.0.jar!/com/my/package/folders/file.end
File test = new File(cFile);
System.out.println("exists: " + test.exists());
System.out.println("test path: " + test.getAbsolutePath());
Not sure why this would be. If Java finds a file, then I would assume that the file exists...
Short answer: don't assume that the "path" of a URL is a file system pathname.
I am trying to load some data into an AWS lambda and am using getClass().getResource() to do so. This returns a nice URL that in logs seemingly prints out a plausible url;
Yes. (It would be nice if you showed us what the original URL looks like ... though I can guess.)
However, when I try and make a file based on that path, I get a file that when I call .exists() returns false.
OK, unless the URL has the protocol "file:", I would NOT expect that to work.
The path in a URL is a path that is intended for the protocol handler to resolve. The idea is that you use URL::openStream to open a stream to the resource named by the URL and then read it. The protocol handler takes care of interpreting the path (etc) and setting up the stream.
For a "file:" URL, the protocol handler will resolve the path in the file system, and provide you a stream to read the file.
For a "http:" URL, the protocol handler establishes a connection to the server, sends a GET request, and returns you a stream to read the response body.
For a "jar:" URL, the protocol handler opens the JAR file, finds the entry within the JAR file, and hands you a stream to read it.
And so on.
If you look at these, it is only in the "file:" case that there is a reasonable expectation that treating the path component of the URL as a file system pathname could work.
Looking at the pathname in your question:
file:/var/task/lib/MyLambda-1.0.jar!/com/my/package/folders/file.end
I surmise that the original URL was:
jar:file:/var/task/lib/MyLambda-1.0.jar!/com/my/package/folders/file.end
So what that says to the "jar:" protocol handler is:
Find the resource identified by the URL "file:/var/task/lib/MyLambda-1.0.jar"
Open it as a JAR file stream
Find the entry "/com/my/package/folders/file.end" in the JAR file's namespace
Open a stream to read that entry's content.
The JAR file protocol handler knows how to do that. But (clearly) the File class doesn't ... because that "path" is not a file system pathname.
How you solve this depends on what you really need.
If you just need a stream to read the resource, use getClass().getResourceAsStream(...) instead.
If it must be a file in the file system, you may have to get hold of the stream (see above), copy it to a temporary file, and use a File for the temporary file.
If you are doing the because you want to write to the "file", I would suggest that you give up on that idea. It is a bad idea for an application to try to update its resources. And in some cases it simply won't / cannot work.
Is your File test = new File(cFile), Is your cFile made correctly with a proper path? Maybe the last print statement is just picking up on the incorrect path you made? But in reality you don't actually have a file there. Have you checked manually?

Java nio: How to add extension to an absolute path?

This feels like it should be something straight forward, but I can seem to find an elegant solution to it without converting to File.
Given a Path
Path path = Paths.get("/a/b/foo")
How to do get the path /a/b/foo.bar? subpath will return a relative path regardless of whether the original path is relative or absolute.
I would prefer not to have to use additional libraries. But, maybe that is the only way?
To change the file name of a Path, use one of the resolveSibling() methods:
This is useful where a file name needs to be replaced with another file name.
Using this method ensures that the result Path object is for the same FileSystem as the source Path object.
So, to add extension ".bar" to a Path:
path = path.resolveSibling(path.getFileName() + ".bar");

filesystem.getPath() returns wrong path

This problem is driving me crazy. I have a file I would like to reach in my src/main/resources folder and I am trying to obtain the path via:
FileSystem fileSystem = FileSystems.getDefault();
Path path = fileSystem.getPath(AnalysisEngine.class.getResource("/models/10_NB_7dev_2.model").getFile());
However, I keep getting the following error:
Illegal char <:> at index 2: /C:/Users/...(the path is here)/models/10_NB_7dev_2.model
As you can see, the path returned has '/' before C:, which ruins everything. What is the reason and how could this be fixed? Is there an alternative with java.io package?
I am using Windows 8 - 64 bit OS, if it helps.
The URL returned by Class#getResource(String) contains a preceding /.
/C:/Users/...(the path is here)/models/10_NB_7dev_2.model
That's just how URLs work. Then the FileSystem tries to parse that, but it makes no sense to it that there is a : character in the mix, so it throws an exception. In other words, getPath() is trying to create a path, not a url. You cannot have a : character in a Windows (possibly linux as well) path, unless it is directly following the Drive name as the first two characters of the path string.
The solution here is not to use the path of a classpath resource. A classpath resource might not come from the filesystem directly, it might be inside a jar.
...(the path is here)/models/10_NB_7dev.model
in your code you put:
("/models/10_NB_7dev_2.model").
Are you meaning to put a _2.?
If you are not worried about using the default filesystem (e.g. if you aren't using an in-memory filesystem for testing) then you can do:
URI uri = AnalysisEngine.class.getResource("/models/10_NB_7dev_2.model").toURI();
Path path = Paths.get(uri);

file.toURI() returning A Wrong value?

I work in a Java RCP application. I am doing the following lines of code:
File file = new File(location);
String filePath = file.toURI().toString();
Desktop desktop = Desktop.getDesktop();
desktop.browse((new URL(filePath)).toURI());
where location is a String.
When the value of location is: http://www.google.com,
file.toURI()
is appending "file:/C:/eclipse%203.7.2/eclipse/" to the value and hence it becomes
file:/C:/eclipse%203.7.2/eclipse/http:/www.google.com
But when the value is: C:\Program Files,
file.toURI()
is not appending anything and returning the same value correctly.
Is there a limitation related to paths starting with http:// or something.
Does anyone have any idea on this ?
We have 2 types of file locations: relative and absolute. When the location is something like C:\User in MS Windows or /home in Linux the location is absolute and there is no need to append something at the beginning of them! But when the location is http://google.com the program append your program location at the beginning of it.
I think you need to search about URI and URL. You used them incorrectly!
java.io.File works with file paths not URLs.
So it transforms the supplied initialization parameters into the representation that is your local file system supports.
"http://" means nothing to your local file system, it's just a file name (well, wrong file name but anyway).
In the first case with "http://www.google.com" it does not see disk drive letter in the supplied value so it is considered as relative path and current working dir absolute path added as a prefix ("user.home" env var if I'm not mistaken).
In the second case, you added an absolute path "C:\Program Files". It sees disk drive letter inside and there is no sense to add anything as a prefix.

Categories