I'm loading a text file from within a package in a compiled JAR of my Java project. The relevant directory structure is as follows:
/src/initialization/Lifepaths.txt
My code loads a file by calling Class::getResourceAsStream
to return a InputStream
.
public class Lifepaths {
public static void execute() {
System.out.println(Lifepaths.class.getClass().
getResourceAsStream("/initialization/Lifepaths.txt"));
}
private Lifepaths() {}
//This is temporary; will eventually be called from outside
public static void main(String[] args) {execute();}
}
The print out will always print null
, no matter what I use. I'm not sure why the above wouldn't work, so I've also tried:
"/src/initialization/Lifepaths.txt"
"initialization/Lifepaths.txt"
"Lifepaths.txt"
Neither of these work. I've read numerous questions so far on the topic, but none of them have been helpful - usually, they just say to load files using the root path, which I'm already doing. That, or just load the file from the current directory (just load filename
), which I've also tried. The file is being compiled into the JAR in the appropriate location with the appropriate name.
How do I solve this?
@Emracool... I'd suggest you an alternative. Since you seem to be trying to load a *.txt file. Better to use FileInputStream()
rather then this annoying getClass().getClassLoader().getResourceAsStream()
or getClass().getResourceAsStream()
. At least your code will execute properly.
What you really need is a full absolute classPath for the file. So instead of guessing it, try to find out the ROOT and then move the file to a better location base one <.war> file structures...
URL test1 = getClass().getResource("/");
URL test2 = getClass().getClassLoader().getResource("/");
URL test3 = getClass().getClassLoader().getResource("../");
logger.info(test1.getPath());
logger.info(test2.getPath());
logger.info(test3.getPath());
So there are several ways to get a resource from a jar and each has slightly different syntax where the path needs to be specified differently.
The best explanation I have seen is this article from InfoWorld. I'll summarize here, but if you want to know more you should check out the article.
Methods
ClassLoader.getResourceAsStream()
.Format: "/"-separated names; no leading "/" (all names are absolute).
Example: this.getClass().getClassLoader().getResourceAsStream("some/pkg/resource.properties");
Class.getResourceAsStream()
Format: "/"-separated names; leading "/" indicates absolute names; all other names are relative to the class's package
Example: this.getClass().getResourceAsStream("/some/pkg/resource.properties");
Updated Sep 2020: Changed article link. Original article was from Javaworld, it is now hosted on InfoWorld (and has many more ads)
There seems to be issue with the ClassLoader that you are using. Use the contextClassLoader to load class. This is irrespective of whether it is in a static/non-static method
Thread.currentThread().getContextClassLoader().getResourceAsStream
......
if you are using Maven make sure your packing is 'jar' not 'pom'.
<packaging>jar</packaging>
Roughly speaking:
getClass().getResource("/")
~= Thread.currentThread().getContextClassLoader().getResource(".")
Suppose your project structure is like the following:
+-- src
¦ +-- main
¦ +-- test
¦ +-- test
¦ +-- java
¦ ¦ +-- com
¦ ¦ +-- github
¦ ¦ +-- xyz
¦ ¦ +-- proj
¦ ¦ +-- MainTest.java
¦ ¦ +-- TestBase.java
¦ +-- resources
¦ +-- abcd.txt
+-- target
+-- test-classes
+-- com
+-- abcd.txt
// in MainClass.java
this.getClass.getResource("/") -> "~/proj_dir/target/test-classes/"
this.getClass.getResource(".") -> "~/proj_dir/target/test-classes/com/github/xyz/proj/"
Thread.currentThread().getContextClassLoader().getResources(".") -> "~/proj_dir/target/test-classes/"
Thread.currentThread().getContextClassLoader().getResources("/") -> null
Make sure your resource directory (e.g. "src") is in your classpath (make sure it's a source directory in your build path in eclipse).
Make sure clazz is loaded from the main classloader.
Then, to load src/initialization/Lifepaths.txt, use
clazz.getResourceAsStream("/initialization/Lifepaths.txt");
Why:
clazz.getResourcesAsStream(foo)
looks up foo from within the classpath of clazz, relative to the directory clazz lives in. The leading "/" makes it load from the root of any directory in the classpath of clazz.
Unless you're in a container of some kind, like Tomcat, or are doing something with ClassLoaders directly, you can just treat your eclipse/command line classpath as the only classloader classpath.
You might want to try this to get the stream i.e first get the url and then open it as stream.
URL url = getClass().getResource("/initialization/Lifepaths.txt");
InputStream strm = url.openStream();
I once had a similar question: Reading txt file from jar fails but reading image works
Don't use absolute paths, make them relative to the 'resources' directory in your project. Quick and dirty code that displays the contents of MyTest.txt from the directory 'resources'.
@Test
public void testDefaultResource() {
// can we see default resources
BufferedInputStream result = (BufferedInputStream)
Config.class.getClassLoader().getResourceAsStream("MyTest.txt");
byte [] b = new byte[256];
int val = 0;
String txt = null;
do {
try {
val = result.read(b);
if (val > 0) {
txt += new String(b, 0, val);
}
} catch (IOException e) {
e.printStackTrace();
}
} while (val > -1);
System.out.println(txt);
}
What worked for me was to add the file under My Project/Java Resources/src
and then use
this.getClass().getClassLoader().getResourceAsStream("myfile.txt");
I didn't need to explicitly add this file to the path (adding it to /src
does that apparently)
Don't know if of help, but in my case I had my resource in the /src/ folder and was getting this error. I then moved the picture to the bin folder and it fixed the issue.
What worked for me is I placed the file under
src/main/java/myfile.log
and
InputStream is = getClass().getClassLoader().getResourceAsStream("myfile.log");
if (is == null) {
throw new FileNotFoundException("Log file not provided");
}
The rules are as follows:
And try:
Lifepaths.class.getResourceAsStream("/initialization/Lifepaths.txt")
instead of
Lifepaths.class.getClass().getResourceAsStream("/initialization/Lifepaths.txt")
(not sure if it makes a difference, but the former will use the correct ClassLoader/ JAR, while I'm not sure with the latter)
Please remove
../src/main/resourcesor include file you are trying to read
The default JVM classloader will use parent-classloader to load resources first: .
Lifepaths.class.getClass()
's classloader is bootstrap classloader
, so getResourceAsStream
will search $JAVA_HOME only, regardless of user provided classpath
. Obviously, Lifepaths.txt is not there.
Lifepaths.class
's classloader is system classpath classloader
, so getResourceAsStream
will search user-defined classpath
and Lifepaths.txt is there.
When using java.lang.Class#getResourceAsStream(String name)
, name which is not start with '/' will be added with package name
as prefix. If you want avoid this, please using java.lang.ClassLoader#getResourceAsStream
.
For example:
ClassLoader loader = Thread.currentThread().getContextClassLoader();
String resourceName = "Lifepaths.txt";
InputStream resourceStream = loader.getResourceAsStream(resourceName);
I found myself in a similar issue. Since I am using maven I needed to update my pom.xml to include something like this:
...
</dependencies>
<build>
<resources>
<resource>
<directory>/src/main/resources</directory>
</resource>
<resource>
<directory>../src/main/resources</directory>
</resource>
</resources>
<pluginManagement>
...
Note the resource tag in there to specify where that folder is. If you have nested projects (like I do) then you might want to get resources from other areas instead of just in the module you are working in. This helps reduce keeping the same file in each repo if you are using similar config data
In pom.xml manage or remove ../src/main/resources
Source: Stackoverflow.com