252

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?

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
  • 5
    Have you checked that it really *is* in the jar file? Have you checked the file casing? – Jon Skeet May 15 '13 at 16:41
  • 1
    @JonSkeet It is indeed being compiled into the JAR file in the appropriate location, and the case is correct. –  May 15 '13 at 16:42
  • 1
    @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!) –  May 15 '13 at 16:49
  • Can you show `Lifepaths.getClass()`? There is no such static method defined in Object... – Puce May 15 '13 at 16:59
  • Are you using any custom classloaders (e.g. via OSGi)? – Puce May 15 '13 at 16:59
  • @Puce Sorry, that one was a typo. Also, no custom classloaders –  May 15 '13 at 17:00
  • Change your code to `Lifepaths.class.getResourceAsStream(...)`, you are trying to load your resource using system class loader – hoaz May 15 '13 at 17:02
  • 1
    Have a look at [this answer](http://stackoverflow.com/a/16564611/418556) & 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. – Andrew Thompson May 15 '13 at 17:36
  • I was stuck on this issue for a long time only for me to realize the path should not contain double slash. `/path//file.txt` will not work but `/path/file.txt` will work and the path is case sensitive. – Thecarisma Feb 09 '20 at 11:17

27 Answers27

224

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

When invoking getResourceAsStream(name), the name must start with "/". I am not sure whether this is necessary, but I have problem without it

Michael
  • 41,989
  • 11
  • 82
  • 128
hoaz
  • 9,883
  • 4
  • 42
  • 53
  • 5
    I have been screwing with this since 8am this/yesterday morning. Saved me. I did also need a leading slash to make it work. – kyle Jan 28 '16 at 06:05
  • 4
    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. – Zon May 26 '17 at 17:28
  • 5
    @David -- I think it (leading '/') is necessary otherwise it searches relative to the Lifepaths.class package – Mz A Apr 09 '18 at 10:43
  • 17
    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`. – Dwhitz Jul 06 '18 at 09:12
  • @Dave Ankin: It is not absurd and certainly not a **Java specific** implementation detail. Care to show us all how simpler it is to acces files from Python or JavaScript, so that we can all be ashame ? – Zartc Apr 01 '21 at 14:51
  • 1
    @Zartc this is not file access, its jar access which is Java Archive - I spent quite a bit of time figuring out what my IDE was doing that i wasn't, super grateful for this post. Lots of people to js/python dev without an ide, using text editors or ipython. – Dave Ankin Apr 01 '21 at 15:22
  • @Dave Ankin: This **is** file access. The file being inside a JAR do not change the fundamental operation. The problem exposed here is that anytime you wana access a file, it can be tricky to know how to reach it from where you are. Point is: this is not a **Java specific detail**, you'll encounter this "implementation detail" with every language and on every platforms (i.e Linux vs Windows do you put C: or not, do you use '/' or '\'). At least Java is nice enought to let you access files inside a JAR (a kind of ZIP file) transparently, which is probably not the case for other environments. – Zartc Apr 01 '21 at 16:04
  • @Zartc but, you are forced to use jars with java. if you are accessing a zip file with any other programming language, then you specify which filename you want. and there is no confusion about which classloader reads which jars, etc etc etc – Dave Ankin Apr 01 '21 at 16:25
  • Avoid the temptation to build the path via `Paths` otherwise it won't work in windows ("/" -> "\") – laffuste Jul 08 '21 at 11:19
70

The rules are as follows:

  1. check the location of the file you want to load inside the JAR (and thus also make sure it actually added to the JAR)
  2. use either an absolute path: path starts at the root of the JAR
  3. 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)

Puce
  • 37,247
  • 13
  • 80
  • 152
  • 2
    I've already done all three of these things. Please reread my question. –  May 15 '13 at 16:51
  • 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) – Puce May 15 '13 at 16:56
  • 1
    Your remark about the relative path finally solved the issue at my end; thanks! – Paul Bormans Apr 29 '16 at 10:36
  • Interesting. It turns out I had to do "/config.properties" (with a slash) to get to it... – Erk Jun 01 '20 at 01:35
60

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

  1. ClassLoader.getResourceAsStream().

Format: "/"-separated names; no leading "/" (all names are absolute).

Example: this.getClass().getClassLoader().getResourceAsStream("some/pkg/resource.properties");

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

greedybuddha
  • 7,488
  • 3
  • 36
  • 50
16

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

Shijing Lv
  • 6,286
  • 1
  • 20
  • 12
15

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);
}
Paul Smith
  • 454
  • 6
  • 11
9

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

Community
  • 1
  • 1
MrD
  • 123
  • 1
  • 8
9

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

plosco
  • 891
  • 1
  • 10
  • 18
8

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

Binita Bharati
  • 5,239
  • 1
  • 43
  • 24
5

The default JVM classloader will use parent-classloader to load resources first: deletegate-parent-classloader.

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); 
gavenkoa
  • 45,285
  • 19
  • 251
  • 303
Ridox
  • 1,073
  • 13
  • 18
4

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)

John Manko
  • 1,828
  • 27
  • 51
Pablo Gonzalez
  • 1,710
  • 3
  • 21
  • 36
3

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());
ConAim
  • 61
  • 3
2

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.

Boris Brodski
  • 8,425
  • 4
  • 40
  • 55
2

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

<packaging>jar</packaging>
Feku279
  • 93
  • 1
  • 10
1

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.

Leo Ufimtsev
  • 6,240
  • 5
  • 40
  • 48
1

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

enter image description here

j5shi
  • 797
  • 1
  • 8
  • 21
1

I know there are different answers here. Mine worked like this. Added the file in the resources directory. And in the executable code I mentioned the path for that file with "/" as prefix within the quotes of the filename.

 InputStream resourceStream = loader.getResourceAsStream("/LifePaths.txt");
0

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");
        }
Akash Yellappa
  • 2,126
  • 28
  • 21
  • 1
    The source folder is called `src/main/JAVA`, obviously your non-code files should not be located here. – leyren Jul 06 '20 at 06:22
0

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

qwebek
  • 949
  • 2
  • 8
  • 15
0

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

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 ~

tcsnzh
  • 9
  • 1
  • 2
    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](/help/how-to-answer). – Community Nov 25 '21 at 00:47
0

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:

enter image description here

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.

mbmast
  • 960
  • 11
  • 25
0

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

Amit
  • 21
  • 1
0

Problem with me was that resource was not in src folder.

R4de
  • 61
  • 1
  • 6
  • 1
    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](/help/how-to-answer). – Community Oct 03 '22 at 08:56
0

Coming late to the party. Depending on the JDK version you are using (maybe it is time to review the StackOverflow 'question already answered'?) JPMS can impact on the resources you are able to load (or not).

Packaging my code as module I had to do that:

Thread.currentThread().getClass().getModule().getResourceAsStream("resourceName");

And then it worked

mrossini
  • 378
  • 3
  • 10
0

What happened to me was an intelliJ (2023.1) issue. I was trying to get a .json file from my /resources folder and I was getting null. The problem was the .json pattern was not in Settings > Build, Execution, Deployment > Compiler > Resource Patterns.

After I added it, all worked fine (without the initial "/", by the way).

See: Java - class.getResource returns null

-1

Please remove

../src/main/resources

or include file you are trying to read

Shahid Hussain Abbasi
  • 2,508
  • 16
  • 10
-1

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

Shahid Hussain Abbasi
  • 2,508
  • 16
  • 10
-12

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

Anshul Goyal
  • 73,278
  • 37
  • 149
  • 186
Jain
  • 65
  • 2
  • 8
  • what ??? -1 for such working answer. No matter what. But above suggested solution will work for sure. – Jain Apr 24 '14 at 05:08