How to use relative path instead of absolute - java

So I need to set an icon for Intellij IDEA plugin, but when I'm trying to get this icon from my project with new File(relative path) or getClass().getResource(relative path). It can't find the files, only works with the absolute path. I have tried with the following relative paths:
images/icon.png
resources/images/icon.png
main/resources/images/icon.png
src/main/resources/images/icon.png
Icons path: src/main/resources/images/icon.png
Source code path: src/main/java/com/timetrack/plugin/MyClass.java
code:
File file = new File("src/main/resources/images/running.png");
BufferedImage img = ImageIO.read(file);
or with this
BufferedImage img = ImageIO.read(getClass().getResource("images/running.png"));
EDIT
Need to mention that I'am using Gradle to build the project. So the output directory looks like this:
Icon path: build/resources/main/images/icon.png
Compiled classes:
build/classes/java/main/com/timetrack/plugin/MyClass.class

Your resource string needs to start with a slash, since it is not in the same package as your class.
Paraphrased from the documentation of getResource:
If the name begins with a /, then the absolute name of the resource is the portion of the name following the /.
Otherwise, the absolute name is of the form modified_package_name/name, where the modified_package_name is the package name of this class with / substituted for ..
In other words, the argument passed to Class.getResource is assumed to be in the same package as the Class itself, unless the argument starts with a slash.
This is because the proper way to include resources in an application or library is to place them in the same package directory as the class that uses them. The reason for doing this is the same reason we use packages. For that matter, it’s the same reason applications don’t store all their files in the user’s home directory or in C:\: because there is a real risk that other programs will choose the same name and will interfere with your program.
Class.getResource searches the classpath for the requested resource. If you package your icon as images/running.png, and a library also decides to package its image as images/running.png, then Class.getResource will search the classpath and return whatever it finds first. Depending on the order of the classpath entries, either you will get the wrong image, or that other library will. The two are essentially stepping on each other.
On the other hand, if you place your image at src/main/resources/com/timetrack/plugin/running.png, it’s unlikely any other code will be using that package, so your odds of a collision are mimimal. And because this was intended to be the most common use case, using it this way is easier: You can retrieve the image URL with just MyClass.class.getResource("running.png").

Related

Relative path for library exporting in Java

I'm trying to export a .JAR to be used as library to other projects. The problem is that I need to use relative paths when referencing files inside this library, but the only solutions I found were using absolute paths like:
private static final String FILE = new File("").getAbsolutePath().concat("/src/bla/file.txt");
Obviously whenever I try to run this line of code as an exported library I'll get something like DRIVE/project/src/bla/file.txt which is not correct since this .JAR can be anywhere inside DRIVE/projects like DRIVE/projects/lib/myLib.jar.
In Nodejs we had easy functions to retrieve relative paths according to the runtime location. How can I reference files in such a way that it will capture the "runtime path" so that I can safely reference them and the path will be dynamically solved?
For those who are so eager to mark this question as duplicate, please read with attention first. I'm NOT asking how to READ files from resources!
To use the "file.txt" present in the classpath,we need to make sure the "file.txt" is present in the directory represented by classpath.
Assume you have all the class files generated in a directory named "/home/abcuser/target".
For simplicity we will place the file.txt in the target directory root level.
The main class is say TestFileAccess.class(the class with the main method)
To execute the main class present in the target directory you can use the below command
java -cp /home/abcuser/target TestFileAccess
Now, the classpath in this case is /home/abcuser/target
To access the resources on classpath,you can go with two ways.
ClassLoader.getSystemResource and ClassLoader.getSystemResourceAsStream methods.
Class.getResource and Class.getResourceAsStream
The main difference between the ClassLoader and Class versions of the methods is in the way that relative paths are interpreted.
The Class methods resolve a relative path in the "directory" that corresponds to the classes package.
The ClassLoader methods treat relative paths as if they were absolute; i.e. the resolve them in the "root directory" of the classpath
Using ClassLoader you can use the below snippet
InputStream inputStream = ClassLoader.getSystemResourceAsStream("file.txt");
To explicitly reference a resource as a classpath file you can add the resource path to the classpath while executing the java code.
Let's say your resource "file.txt" is in /home/abcuser/resources.
You can add the the resource path to the classpath during the java execution start as shown below
java -cp "/home/abcuser/target:/home/abcuser/resources" TestFileAccess

Scanner cannot find (correct) file path

I'm programming in Java with IntelliJ and have been trying to use the Scanner class to read the file. Even with the correct path, I still get a "No such file or directory" error. Does anyone have any suggestions?
My working directory is /Users/kevinliu/Desktop/test
Here is a picture of how the project is set-up.
Are you trying to create a swing/console application using maven?
If yes, maven is not able to find the source. You have to add it on the pom file. See here on how to add it on pom file.
if no, do you have rights to access the address of the image file? Some times, folder are protected by the OS.
You can also use YourClassName.class.getResource("input/input1.txt") to locate file/s under the directory that your class was in.
Even with the correct path, I still get a "No such file or directory" error.
The path is NOT correct. That path says look for a directory called "src" in the root directory of your computer. That is almost certainly not where the input file lives.
If you are going to use an absolute pathname for a file within the working directory that you stated, it should look like this:
/Users/kevinliu/Desktop/test/src/input/input1.txt
(You can check what it will actually be using a file browser ... outside of Intellij.)
If you want to use a relative pathname, try this
src/input/input1.txt
Notes:
There is no leading "/" on a relative pathname. A leading "/" means it is an absolute pathname. Absolute pathnames start at the root directory.
A relative path is resolved relative to the >>current<< working directory. That will depend on where and how you run the application ...
For a production application, you would not want to refer to a file in the source tree. The end user typically won't have the source tree.
Consider making the path a command line argument or configuration setting for your application.
Consider making the file a "resource" that is part of the application's JAR file. (You would open it a different way ...)
If you ever get a "No such file or directory" message, that means that the path is not correct in some sense. You might be in the wrong place, you might not have permission on a parent directory, the file may have been removed or renamed, there may be a you, or something else. Either way, that error comes from the operating system and the OS doesn't make mistakes about these things. The mistake will be yours (or the user's).

Reading file form classpath

I have a maven project and want to read file in it form its class path. The code that i am using is
InputStream is = getClass().getResourceAsStream ("filename.json");
But every time i am getting null inputstreams. I am not sure why ?
The file is places under /src/main/resources. The same folder which contains log4j.xml and it is being picked up decently.
Please note, I am trying to run this file from Eclipse i.e., run or debug mode. No vm arguments or whatsoever.
The Class.getResourceAsStream(String) method looks for the given resource within the same namespace (i.e. package) that the given class is in unless you give it an absolute path (see the API documentation); If it can't find the resource on the classpath in this namespace, it returns null. Since your class is likely inside e.g. com.myproject.resourcemanagement, your resource file has to analogously be under src/main/resources/com/myproject/resourcemanagement, similary to how your class source files are organised (under src/main/java/com/myproject/resourcemanagement).

Why my Jar doesn't run unless I extract files?

Every time I run the exported .jar file, that contains a JFrame with an image as its icon, the file doesn't run, unless I extract the file. In the compiler it is running. I dont want to make a launcher that saves both, the resources package and the jar file, in a directory.
"Why my Jar doesn't run unless I extract files?"
This seems to be the behavior of using File to your resources. Take for example
File file = new File("resources/image.png");
Image image = ImagIO.read(file);
And you project structure (Note the resources should actually be in the src, so that it builds into the jar automatically - unless you configure it differently. But for the sake of this argument, let's say you do confgigure it where resources is built to the jar)
C:\
Project
resources\image.png
Some examination:
Run from IDE - WORKS! Why? Using File looks for files on the file system. Using a relative path, the search will begin from the "working directory", which in the case of the IDE in generally the project root. So "resources/image.png" is a valid path, relative to ProjectRoot
Build jar, say it ends up in a dist dir in the project. This is what it looks like
ProjectRoot
dist
ProjectRoot.jar
Now for the sake of this argument (and is actually the correct way), let's try and print the URL of the resource in out program, so that when you run the jar, it prints out the URL of the file
URL url = Test.class.getResource("/resources/image.png");
System.out.println(url.toString());
When we run the jar C:\ProjectRoot\dist> java -jar ProjectRoot.jar We will see the print out C:\ProjectRoot\dist\ProjectRoot.jar!\resources\image.png. You can obviously see even though the current working directory is the location of the jar, the paths no longer match, with the added jar ProjectRoot.jar! location.
So why does it work when we extract it. Well when you extract it, then the path is correct
C:\ProjectRoot
dist
resources/image.png // from extracted jar
ProjectRoot.jar
When you run from the C:\ProjectRoot\dist >, the resource dir is where is should be.
Ok enough with the explanation.
For this reason, when you want to read embedded resources, they should be read from an URL as Andrew Thompson mentioned. This url should be relative to the class calling it, or the class loader. Here are a couple different ways:
As shown already
URL url = getClass().getResource("/resources/image.png");
Notice the /. This will bring us to the root of the classpath, where the resources dir will be. URL can be passed to many constructors, like ImageIcon(URL) or `ImageI.read(URL)
You can use:
InputStream is = getClass().getResourceAsStream("/resources/image.png");
Which will use an URL under the hood. You can use InputStream with many constructors also.
There's also ways to use the class loader, which will start at the root, so you don't need the /
URL url = getClass().getClassLoader().getResource("resources/image.png");
So there are a few ways you can go about it. But in general, reading File with hard coded string paths is never a good idea, when using embedded resources. It's possible to obtain the path dynamically so you can use File, but you will still need to use one of the aforementioned techniques, which unless you really need a File would be pointless, as you can do what you need with the InputStream or URL
To make a long story short
This would work
ProjectRoot
src\resources\image.png
URL url = getClass().getResource("/resources/image.png");
Image image = ImageIO.read(url);

Intellij can't find .png

My intellij can't find local .png images.
private String craft = "craft.png";
ImageIcon ii = new ImageIcon(this.getClass().getResource(craft));
The .png is located in the same directory as the java files. I don't understand why it isn't working. Using maven build, tried alternating from resources to java, but still no luck :(
craft.png must be placed into src/main/resources, otherwise it will be not copied to the classpath according to the Maven rules. See this answer for more details.
Your code should be also changed to:
private String craft = "/craft.png";
Here is the sample working project.
Go to your IntelliJ Preferences and search for "resource patterns" (or just go straight to the "Compiler" settings).
IntelliJ will only copy certain resources to the output directory. Make sure the resource pattern includes *.png.
I have my resource pattern set to !*.java (copy everything that's not a source file) which seems to work fine (and should really be the default, in my opinion).
tried alternating from resources to java
So at first you tried putting craft.png into src/main/resources. That is where it must be put according to Maven (not in src/main/java).
But it didn't work because
this.getClass().getResource("craft.png") tries to find "craft.png" relative to the this.class's package. If your this.class is in package foo.bar then you must put craft.png in src/main/resources/foo/bar/
You can also provide an absolute path in getResource() by using a leading slash /. For example put craft.png into a custom folder under resources src/main/resources/customFolder/ and read it with the leading slash / in front of customFolder:
this.getClass().getResource("/customFolder/craft.png")
If you don't use leading slash in getResource() method then internally class's package name is prepended to the resource name to make it absolute.
This behavior is explained in Class.getResource()

Categories

Resources