Absolute path of a file inside a project package - java

I've created a file inside a project package using this code:
File xmlFile = new File("src/com/company/project/xml/tags.xml");
I am able to read the file while running from eclipse. However, after creating .jar, I'm unable to read the file. So I want to put absolute path while reading the file from the project package. How it can be done? Help and suggestions are appreciated.

In most cases, IDE's will include no Java files in the resulting Jar. Most IDE's will also include the src directory in the classpath when you run/debug the program from within them.
As a general rule of thumb, never include src in any path, src will simply not exist once the program is built.
Instead you need to make use of Class#getResource or Class#getResourceAsStream, depending on your needs. You should remember, you should never treat an "embedded" resource as a File, as in most cases it won't be, it'll be a stream of bytes in a zip file.
Something like...
URL xmlFile = getClass().getResource("/com/company/project/xml/tags.xml");
will return a URL reference to the resource. Remember, if you need a InputStream, you'll have to Class#getResourceAsStream.
If you want the resource to be writable, then you will need to find a different location to store it, as embedded resources are read only

Try with getClass().getResource()
new File(getClass().getResource("src/com/company/project/xml/tags.xml").toURI());

Related

Spring does not find file in resources

i have following line
File file = ResourceUtils.getFile("classpath:calculation.csv");
and i also tried
File file = ResourceUtils.getFile("classpath:/calculation.csv");
but both will throw an error
java.io.FileNotFoundException: class path resource [calculation.csv] cannot be resolved to absolute file path because it does not exist
but i do have calculation.csv in by resources folder..
why is this?
I need to read file from resources folder, and it should also work in server enviroment
EDIT:
ClassLoader classLoader = getClass().getClassLoader();
File file = new File(classLoader.getResource("calculation.csv").getFile());
works just as fine, so not at all..
EDIT2:
tried with folder.. i have both calculation.csv and csv/calculation.csv in my resources folder now..
none of the above work, with /csv/ added.
what kind of path does this thing want?!
EDIT3:
aaand
File file = new ClassPathResource("calculation.csv").getFile();
is also no go, what even is this..
Loading file (as FILE) wont work. You must use it as resource. Files inside JAR will not work as file anyway. This is also what your "check" code shows.
classLoader.getResource("calculation.csv") works, because you are using classloader to get resource, not filesystem to get file (which is what File api does). It could work, if you would deal with non packed application. Once you pack your app into JAR, file path will be like your/path/to/jar.jar!someResource - note ! mark (and that is what you would see as well). So basicly it will return File instance, you that you wont be able to use anyway, as file system has no access to it.
You could alternatively try to extract it first with ResourceUtiuls#extractJarFileURL(URL jarUrl) and then use extracted file.
I think, that in most cases Class#getResourceAsStream is the way to go and I think that it should fit your needs as well to read content of resource.

Using relative path in a maven project

I have a maven project with these standard directory structures:
src/main/java
src/main/java/pdf/Pdf.java
src/test/resources
src/test/resources/files/x.pdf
In my Pdf.java,
File file = new File("../../../test/resources/files/x.pdf");
Why does it report "No such file or dirctory"? The relative path should work. Right?
Relative paths work relative to the current working directory. Maven does not set it, so it is inherited from whatever value it had in the Java process your code is executing in.
The only reliable way is to figure it out in your own code. Depending on how you do things, there are several ways to do so. See How to get the real path of Java application at runtime? for suggestions. You are most likely looking at this.getClass().getProtectionDomain().getCodeSource().getLocation() and then you know where the class file is and can navigate relative to that.
Why does it report "No such file or dirctory"? The relative path should work. Right?
wrong.
Your classes are compiled to $PROJECT_ROOT/target/classes
and your resources are copied to the same folder keeping their relative paths below src/main/resources.
The file will be located relative to the classpath of which the root is $PROJECT_ROOT/target/classes. Therefore you have to write in your Pdf.java:
File file = new File("/files/x.pdf");
Your relative path will be evaluated from the projects current working directory which is $PROJECT_ROOT (AFAIR).
But it does not matter because you want that to work in your final application and not only in your build environment. Therefore you should access the file with getClass().getResource("/path/to/file/within/classpath") which searches the file in the class path of which the root is $PROJECT_ROOT/target/classes.
No the way you are referencing the files is according to your file system. Java knows about the classpath not the file system if you want to reference something like that you have to use the fully qualified name of the file.
Also I do not know if File constructor works with the classpath since it's an abstraction to manage the file system it will depend where the application is run from. Say it is run from the target directory at the same level as source in that case you have to go one directory up and then on src then test the resources the files and finally in x.pdf.
Since you are using a resources folder I think you want the file to be on the classpath and then you can load a resource with:
InputStream in = this.getClass().getClassLoader()
.getResourceAsStream("<path in classpath>");
Then you can create a FileInputStream or something to wrap around the file. Otherwise use the fully qualiefied name and put it somewere like /home/{user}/files/x.pdf.

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);

Why does getResourceAsStream() work in the IDE but not the JAR?

I just want to read a file into my program. The file is located one directory above the working directory at "../f.fsh". So the following code runs correctly when I run it in the IDE
String name="../f.fsh";
InputStream is = getClass().getResourceAsStream(name);
InputStreamReader isreader=new InputStreamReader(is);//CRASHES HERE WITH NULL POINTER EXCEPTION
BufferedReader br = new BufferedReader(isreader);
but when I create a JAR file that has f.fsh zipped inside of it and run it, it crashes when creating the InputStreamReader, because the InputStream is null.
I've read a bunch of answers to questions about input streams and JAR files, and what I got out of it is that I should be using relative paths, but I am already doing that. From what I understand getResourceAsStream() can find files relative to the root of the project, that is what I want. Why does it not work in the JAR? What is going wrong, how can I fix it?
Does it have to do with the classpath? I thought that was only for including files external to the jar being run.
I have also tried, but still fail, when putting a slash in:
InputStream is = getClass().getResourceAsStream("\\"+name);
I looked at: How to get a path to a resource in a Java JAR file andfound that contents of a JAR may not necesarily be accesible as a file. So I tried it with copying the file relative to the jar (one directory up from the jar), and that still fails. In any case I'd like to leave my files in the jar and be able to read them there. I don't know what's going wrong.
You can't use .. with Class.getResourceAsStream().
To load a resource f.fsh in the same package as the class, use SomeClass.class.getResourceAsStream("f.fsh")
To load a resource f.fsh in a sub-package foo.bar of the package of the class, use SomeClass.class.getResourceAsStream("foo/bar/f.fsh")
To load a resource f.fsh in any package com.company.foo.bar, use SomeClass.class.getResourceAsStream("/com/company/foo/bar/f.fsh")
This is described in the javadoc of the getResource() method, although it lacks examples.
If .. works in Class.getResourceAsStream() while running from Eclipse, it's a bug in Eclipse. Eclipse and other IDEs implement custom class loaders to fetch resources from the project at runtime. It looks like the class loader implementation in Eclipse isn't performing all the necessary validations on input to getResourceAsStream() method. In this case the bug is in your favor, but you will still need to rethink how you structure your resources for your code to work in all cases.
it's mandatory that the name of the file is CASE SENSITIVE
it's mandatory to refresh (F5) the project explorer if the file is moved or copied outside Exclipse

How to specify relative file path in Java file so that it can still work after the file is put in jar file?

Suppose I have a Java class that needs to access a file with absolute path
/home/gem/projects/bar/resources/test.csv:
package com.example
class Foo {
String filePath = ????? // path to test.csv
String lines = FileInputStream(new File(filePath).readAllLines();
}
Where the path to Foo.java is /home/gem/projects/bar/src/com/example.
Of course I cannot specify absolute path to the resource file. This is because jar file will be distributed as library for any clients to use in their own environments.
Assume the resource file like test.csv is always in the same path relative to project root. When a jar is created containing Foo.class, this jar also contains test.csv in the same relative path ( relative to project root).
What is the way to specify relative path that would work no matter where the project bar is moved to? Also how can I create a jar file (which can be in any location) so that the path to the resource file test.csv would still be correct.
To keep things simple, I have used invalid Java API ( readAllLines() which reads all the lines and return a string containing entire file content. Also not using try/catch).
Assume csv file can be read as well as written to.
I hope this makes it clear now.
Put the test.csv file into the src folder and use this:
Foo.class.getResourceAsStream("/test.csv")
To get an InputStream for the file. This will work wherever the project is moved, including packaged as a JAR file.
Example:
ProjectX\src\Test.java
ProjectX\resources\config.properties
If you have the above structure and you want to use your config.properties file, this is how you do it:
InputStream input = new FileInputStream("./resources/config.projects");
In this example you don't have to worry about packaging your source into jar file. You can still modify your resources folder anytime.
Use getResource(), as shown here.

Categories

Resources