1. Overview

In this tutorial, we’ll explore an issue that can come up when reading resource files in a Java application: At runtime, the resource folder is seldom in the same location on disk as it is in our source code.

Let’s see how Java allows us to access resource files after our code has been packaged.

2. Reading Files

Let’s say our application reads a file during startup:

try (FileReader fileReader = new FileReader("src/main/resources/input.txt"); 
     BufferedReader reader = new BufferedReader(fileReader)) {
    String contents = reader.lines()
      .collect(Collectors.joining(System.lineSeparator()));
}

If we run the above code in an IDE, the file loads without an error. This is because our IDE uses our project directory as its current working directory and the src/main/resources directory is right there for the application to read.

Now let’s say we use the Maven JAR plugin to package our code as a JAR.

When we run it at the command line:

java -jar core-java-io2.jar

We’ll see the following error:

Exception in thread "main" java.io.FileNotFoundException: 
    src/main/resources/input.txt (No such file or directory)
    at java.io.FileInputStream.open0(Native Method)
    at java.io.FileInputStream.open(FileInputStream.java:195)
    at java.io.FileInputStream.<init>(FileInputStream.java:138)
    at java.io.FileInputStream.<init>(FileInputStream.java:93)
    at java.io.FileReader.<init>(FileReader.java:58)
    at com.baeldung.resource.MyResourceLoader.loadResourceWithReader(MyResourceLoader.java:14)
    at com.baeldung.resource.MyResourceLoader.main(MyResourceLoader.java:37)

3. Source Code vs Compiled Code

When we build a JAR, the resources get placed into the root directory of the packaged artifacts.

In our example, we see the source code setup has input.txt in src/main/resources in our source code directory.

In the corresponding JAR structure, however, we see:

META-INF/MANIFEST.MF
META-INF/
com/
com/baeldung/
com/baeldung/resource/
META-INF/maven/
META-INF/maven/com.baeldung/
META-INF/maven/com.baeldung/core-java-io-files/
input.txt
com/baeldung/resource/MyResourceLoader.class
META-INF/maven/com.baeldung/core-java-io-files/pom.xml
META-INF/maven/com.baeldung/core-java-io-files/pom.properties

Here, input.txt is at the root directory of the JAR. So when the code executes, we’ll see the FileNotFoundException.

Even if we changed the path to /input.txt the original code could not load this file as resources are not usually addressable as files on disk. The resource files are packaged inside the JAR and so we need a different way of accessing them.

4. Resources

Let’s instead use resource loading to load resources from the classpath instead of a specific file location. This will work regardless of how the code is packaged:

try (InputStream inputStream = getClass().getResourceAsStream("/input.txt");
    BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
    String contents = reader.lines()
      .collect(Collectors.joining(System.lineSeparator()));
}

ClassLoader.getResourceAsStream() looks at the classpath for the given resource. The leading slash on the input to getResourceAsStream() tells the loader to read from the base of the classpath. The contents of our JAR file are on the classpath, so this method works.

An IDE typically includes src/main/resources on its classpath and, thus, finds the files.

5. Conclusion

In this quick article, we implemented loading files as classpath resources, to allow our code to work consistently regardless of how it was packaged.

As always, the example code is available over on GitHub.