ChatGPT解决这个技术问题 Extra ChatGPT

getResourceAsStream returns null

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?

Have you checked that it really is in the jar file? Have you checked the file casing?
@JonSkeet It is indeed being compiled into the JAR file in the appropriate location, and the case is correct.
@greedybuddha While I can't invoke that from a static context, I can invoke it using Lifepaths.class. That being said, why does getClassLoader() allow it to work? (Also, feel free to post an answer!)
Can you show Lifepaths.getClass()? There is no such static method defined in Object...
Have a look at this answer & see if you can get it working using getResource(String). BTW - I have always had problems getting either of those to work in a static context. The problem is basically that the class loader obtained is is the one intended for J2SE classes. You need to get access to the context class loader which is intended for the application itself.

h
hoaz

Lifepaths.class.getClass().getResourceAsStream(...) loads resources using system class loader, it obviously fails because it does not see your JARs

Lifepaths.class.getResourceAsStream(...) loads resources using the same class loader that loaded Lifepaths class and it should have access to resources in your JARs


Just to add on, When invoking getResourceAsStream(name), the name must start with "/". I am not sure whether this is necessary, but I have problem without it.
I have been screwing with this since 8am this/yesterday morning. Saved me. I did also need a leading slash to make it work.
Also keep in mind that the desired source can be outside of the packages hierarchy. In this case you'll have to use "../" in your path to get up one level and then down to another path branch to reach your resource.
@David -- I think it (leading '/') is necessary otherwise it searches relative to the Lifepaths.class package
Just to add some info, you need to add a / before your path if your file is under a different directory; for example initialization/Lifepaths.txt. If the path of the file is the same of yout class (but under resources as main dir) you can just put the name of the file without any /. For example if your class has the following path src/main/java/paths/Lifepaths.java, your file has to have this path src/main/resources/paths/Lifepaths.txt.
P
Puce

The rules are as follows:

check the location of the file you want to load inside the JAR (and thus also make sure it actually added to the JAR) use either an absolute path: path starts at the root of the JAR use an relative path: path starts at the package directory of the class you're calling getResource/ getResoucreAsStream

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)


I've already done all three of these things. Please reread my question.
From your question it's not clear what "The relevant directory structure" is and if you've actually checked if and where the file is located in the JAR (step 1)
Your remark about the relative path finally solved the issue at my end; thanks!
Interesting. It turns out I had to do "/config.properties" (with a slash) to get to it...
g
greedybuddha

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)


your second example is broken
Second example isn't broken, as I said in the answer it depends on where the resource is located.
Also: make sure that your IDE sees the file ('some/pkg/resource.properties') by refreshing the source folder.
The link to JavaWorld is broken.
In case it helps anyone, the root for me /target/classes. So if I load file "test.json" with method one, the file needs to be in /target/classes/test.json in the compiled code or respective jar file.
P
Paul Smith

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

S
Shijing Lv

Roughly speaking:

getClass().getResource("/") ~= Thread.currentThread().getContextClassLoader().getResource(".")

Suppose your project structure is like the following:

├── src
│   ├── main
│   └── test
│       ├── java
│       │   └── com
│       │       └── github
│       │           └── xyz
│       │               └── proj
│       │                   ├── MainTest.java
│       │                   └── TestBase.java
│       └── resources
│           └── abcd.txt
└── target
    └── test-classes  <-- this.getClass.getResource("/")
        │              `--Thread.currentThread().getContextClassLoader().getResources(".")
        ├── com
        │   └── github
        │       └── xyz
        │           └── proj  <-- this.getClass.getResource(".")
        │               ├── MainTest.class
        │               └── TestBase.class
        └── resources
            └── abcd.txt

// in MainTest.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


this explanation clarified a lot the issue for me. Thanks!
Never realised that the getResource method of the Class doesn't have the same root that the getResources method of the ClassLoader. This answer should be at the top of the page.
Saved my life!!!! The visual answer is the best part
C
Community

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


this is exactly what getResourceAsStream() does
p
plosco

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


B
Binita Bharati

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......


g
gavenkoa

https://jrebel.com/wp-content/uploads/2013/08/classloader-hierarchy.jpg

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), a name which doesn't start with '/' will be added with package name as a prefix. To avoid this use java.lang.ClassLoader#getResourceAsStream instead.

For example:

ClassLoader loader = Thread.currentThread().getContextClassLoader();
String resourceName = "Lifepaths.txt";
InputStream resourceStream = loader.getResourceAsStream(resourceName); 

J
John Manko

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)


Wrong java syntax
L
Leo Ufimtsev

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.


B
Boris Brodski

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.


F
Feku279

if you are using Maven make sure your packing is 'jar' not 'pom'.

<packaging>jar</packaging>

C
ConAim

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

A
Akash Yellappa

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 source folder is called src/main/JAVA, obviously your non-code files should not be located here.
t
tcsnzh

jdk >=9 需要在module-info模块中把非根目录/非共父目录的包给它open了。

jdk >=9 need open resource dir in module-info.java , such as :

src java resoureces conf config.json log4j2.xml openapi.yaml

java

resoureces conf config.json log4j2.xml openapi.yaml

conf config.json

config.json

log4j2.xml

openapi.yaml

If you need read conf/config.json , you need two step :

// in module-info.java
module your.mod.name {
    open conf;
}

// then in java code
getClassLoader().getResourceAsStream("conf/config.json");

Else if you need read other in root , it's only :

getClassLoader().getResourceAsStream("openapi.yaml");

you can see {@link java.lang.ClassLoader#getResourceAsStream(String)} known why ~


As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.
S
Shahid Hussain Abbasi

Please remove

or include file you are trying to read


S
Shahid Hussain Abbasi

In pom.xml manage or remove ../src/main/resources


q
qwebek

My caller class was in src/main/...

What helped me to load resource from src/test/resources/folder/file.properties

`properties.load(getClass().getClassLoader().getResourceAsStream("folder/file.properties"));`

https://howtodoinjava.com/java/io/read-file-from-resources-folder/

Java 11


m
mbmast

While not intended to be an answer, I'm presenting this suggestion as an answer so I can get the screenshot in.

For those of you developing on a Windows platform, I highly recommend the Microsoft utility ProcMon, or Process Monitor.

Process Monitor is one utility in a suite of utilities Microsoft gives away (you don't even need to be signed in to download). They are all available at sysinternals.com, the original host that Microsoft acquired and just kept.

Process Monitor can monitor a ton of events that are associated with any running process. If you specify the File Open event and provide the file name (or part of the file name), it will log which process attempted to open the file and it will show every directory searched. This can be useful when you don't know where your running code is looking for the file you specified in your source/config. Here's an example:

https://i.stack.imgur.com/34IQC.png

Here I'm looking for (and not finding) a schema file (XSD) so that the code can use it to validate some user supplied XML.


j
j5shi

If you are using IDEA, make sure to mark your resources folder as 'Resources' so that the path can be recognized by IDE correctly.

Let's say you have a file named 'book.json' in 'resources/', then to use the resources in it, you can do it like this

InputStream input = Main.class.getResourceAsStream("/book.json");

https://i.stack.imgur.com/4bXsr.png


A
Amit

Lifepaths.class.getClass(). getResourceAsStream("Lifepaths.txt"));


A
Anshul Goyal

@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 ??? -1 for such working answer. No matter what. But above suggested solution will work for sure.