In Java, we can use getResourceAsStream or getResource to read a file or multiple files from a resources folder or root of the classpath.

The getResourceAsStream method returns an InputStream.

  // the stream holding the file content
  InputStream is = getClass().getClassLoader().getResourceAsStream("file.txt");

  // for static access, uses the class name directly
  InputStream is = JavaClassName.class.getClassLoader().getResourceAsStream("file.txt");

The getResource method returns an URL and normally convert it to a File; Not working in JAR file.

  // get the file url, not working in JAR file.
  URL resource = getClass().getClassLoader().getResource("file.txt");
  if (resource == null) {
      throw new IllegalArgumentException("file not found!");
  } else {

      // failed if files have whitespaces or special characters
      //return new File(resource.getFile());

      return new File(resource.toURI());
  }

  // for static access
  // URL resource = JavaClassName.class.getClassLoader().getResource("fileName");

1. Files in resources folder

1.1 Review the files in the src/main/resources, later we will access the files and print out the file content.

src/main/resources/database.properties

datasource.url=jdbc:mysql://localhost/favtuts?useSSL=false
datasource.username=root
datasource.password=password
datasource.driver-class-name=com.mysql.jdbc.Driver

src/main/resources/json/file1.json

{
  "name": "favtuts",
  "age": 38
}

src/main/resources/json/file2.json

{
  "name": "jack",
  "age": 40
}

src/main/resources/json/sub/subfile1.json

{
  "name": "sub",
  "age": 99
}

1.2 By default, build tools like Maven, Gradle, or common Java practice will copy all files from src/main/resources to the root of target/classes or build/classes. So, when we try to read a file from src/main/resources, we read the file from the root of the project classpath.

1.3 Below is a JAR file structure. Usually, the files in the resources folder will copy to the root of the classpath.

$ jar -tf target/example.jar

META-INF/MANIFEST.MF
META-INF/
json/
json/sub/
json/file2.json
json/sub/subfile1.json
json/file1.json
database.properties
com/
com/favtuts/
com/favtuts/io/
com/favtuts/io/utils/
//...

2. Get a file from the resources folder.

2.1 The below example demonstrates the use of getResourceAsStream and getResource methods to read a file json/file1.json from the resources folder and print out the file content.

Note

  • The getResource method is not working in the JAR file.
  • The getResourceAsStream method works everywhere.

FileResourcesUtils.java

package com.favtuts.io.utils;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.List;

public class FileResourcesUtils {

    public static void main(String[] args) throws Exception {

        FileResourcesUtils app = new FileResourcesUtils();

        //String fileName = "database.properties";
        String fileName = "json/file1.json";

        System.out.println("getResourceAsStream : " + fileName);
        InputStream is = app.getFileFromResourceAsStream(fileName);
        printInputStream(is);

        System.out.println("\ngetResource : " + fileName);
        File file = app.getFileFromResource(fileName);
        printFile(file);

    }

    // get a file from the resources folder
    // works everywhere, IDEA, unit test and JAR file.
    private InputStream getFileFromResourceAsStream(String fileName) {

        // The class loader that loaded the class
        ClassLoader classLoader = getClass().getClassLoader();
        InputStream inputStream = classLoader.getResourceAsStream(fileName);

        // the stream holding the file content
        if (inputStream == null) {
            throw new IllegalArgumentException("file not found! " + fileName);
        } else {
            return inputStream;
        }

    }

    /*
        The resource URL is not working in the JAR
        If we try to access a file that is inside a JAR,
        It throws NoSuchFileException (linux), InvalidPathException (Windows)

        Resource URL Sample: file:java-io.jar!/json/file1.json
     */
    private File getFileFromResource(String fileName) throws URISyntaxException{

        ClassLoader classLoader = getClass().getClassLoader();
        URL resource = classLoader.getResource(fileName);
        if (resource == null) {
            throw new IllegalArgumentException("file not found! " + fileName);
        } else {

            // failed if files have whitespaces or special characters
            //return new File(resource.getFile());

            return new File(resource.toURI());
        }

    }

    // print input stream
    private static void printInputStream(InputStream is) {

        try (InputStreamReader streamReader =
                    new InputStreamReader(is, StandardCharsets.UTF_8);
             BufferedReader reader = new BufferedReader(streamReader)) {

            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }

        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    // print a file
    private static void printFile(File file) {

        List<String> lines;
        try {
            lines = Files.readAllLines(file.toPath(), StandardCharsets.UTF_8);
            lines.forEach(System.out::println);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

Output

getResourceAsStream : json/file1.json
{
  "name": "favtuts",
  "age": 38
}

getResource : json/file1.json
{
  "name": "favtuts",
  "age": 38
}

2.2 Now, we pack the project into a JAR file and run it; this time, the getResource will fail and returns either NoSuchFileException or InvalidPathException. We cannot read the files inside the JAR file via the resource URL.

Run the JAR file on Linux (Ubuntu), it throws NoSuchFileException.

$ mvn clean package

$ java -jar target/java-io.jar
$ jar tf target/java-io.jar | more
$ java -cp target/java-io.jar com.favtuts.io.utils.FileResourcesUtils

getResourceAsStream : json/file1.json
{
  "name": "favtuts",
  "age": 38
}

# for new File(resource.getFile());
getResource : json/file1.json
java.nio.file.NoSuchFileException: file:/home/favtuts/projects/core-java/java-io/target/java-io.jar!/json/file1.json
	at java.base/sun.nio.fs.UnixException.translateToIOException(UnixException.java:92)
	at java.base/sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:111)
	at java.base/sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:116)
	at java.base/sun.nio.fs.UnixFileSystemProvider.newByteChannel(UnixFileSystemProvider.java:219)
	at java.base/java.nio.file.Files.newByteChannel(Files.java:370)
	at java.base/java.nio.file.Files.newByteChannel(Files.java:421)
	at java.base/java.nio.file.spi.FileSystemProvider.newInputStream(FileSystemProvider.java:420)
	at java.base/java.nio.file.Files.newInputStream(Files.java:155)
	at java.base/java.nio.file.Files.newBufferedReader(Files.java:2838)
	at java.base/java.nio.file.Files.readAllLines(Files.java:3329)
	at com.favtuts.io.utils.FileResourcesUtils.printFile(FileResourcesUtils.java:135)
	at com.favtuts.io.utils.FileResourcesUtils.main(FileResourcesUtils.java:24)

# for new File(resource.toURI());
getResource : json/file1.json
Exception in thread "main" java.lang.IllegalArgumentException: URI is not hierarchical
	at java.base/java.io.File.<init>(File.java:420)
	at com.favtuts.io.utils.FileResourcesUtils.getFileFromResource(FileResourcesUtils.java:112)
	at com.favtuts.io.utils.FileResourcesUtils.main(FileResourcesUtils.java:29)

You may get the following message


    no main manifest attribute, in "java-io.jar"

To fix the issue, read the guides in Stackoverflow: Can’t execute jar- file: “no main manifest attribute”

For Maven, we shoud use the maven-jar-plugin that is defined in pom.xml:

        <!-- Make this jar executable -->
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-jar-plugin</artifactId>
          <version>3.2.0</version>
          <configuration>
              <excludes>
                  <exclude>**/log4j.properties</exclude>
              </excludes>
              <archive>
                  <manifest>
                      <addClasspath>true</addClasspath>                                            
                      <mainClass>com.favtuts.io.utils.FileResourcesUtils</mainClass>                      
                      <classpathPrefix>dependency-jars/</classpathPrefix>
                  </manifest>
              </archive>
          </configuration>
        </plugin>

P.S This example uses the Maven plugin maven-jar-plugin to create the JAR file.

To check the mainClass in jar file:

$ jar tf target/java-io.jar | more

3. Get a file from the resources folder – Unit Test

3.1 We put the test resources at folder src/test/resources for unit tests. Usually, the files in test resources will copy to the target/test-classes folder.

src/test/resources/json/file1.json

{
  "name": "unit test",
  "age": 38
}

src/test/resources/database.properties

datasource.url=jdbc:mysql://localhost/test?useSSL=false
datasource.username=test
datasource.password=password
datasource.driver-class-name=com.mysql.jdbc.Driver

3.2 It works the same way we read the file from src/main/resources. We use the same getResourceAsStream and getResource methods to read the file from the src/test/resources.

FileResourcesTest.java

package com.favtuts.io;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.List;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

// Unit test class
public class FileResourcesTest {

    @DisplayName("Test loading a JSON file")
    @Test
    void loadJSONTest() {

        String fileName = "json/file1.json";

        ClassLoader classLoader = getClass().getClassLoader();

        try (InputStream inputStream = classLoader.getResourceAsStream(fileName);
            InputStreamReader streamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
            BufferedReader reader = new BufferedReader(streamReader)
        ) {
            
            String line;
            while((line = reader.readLine()) != null) {
                System.out.println(line);
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    @DisplayName("Test loading a properties file")
    @Test
    void loadPropTest() throws IOException, URISyntaxException {

        String fileName = "database.properties";

        ClassLoader classLoader = getClass().getClassLoader();

        URL resource = classLoader.getResource(fileName);
        if (resource == null) {
            throw new IllegalArgumentException("file not found! " + fileName);
        }

        //File file = new File(resource.getFile());
        File file = new File(resource.toURI());

        List<String> lines = Files.readAllLines(file.toPath(), StandardCharsets.UTF_8);
        lines.forEach(System.out::println);
    }
    
}

Output

{
  "name": "unit test",
  "age": 38
}

datasource.url=jdbc:mysql://localhost/test?useSSL=false
datasource.username=test
datasource.password=password
datasource.driver-class-name=com.mysql.jdbc.Driver

4. Get all files from a resource folder. (NON-JAR environment)

If we don’t know the exact filename and want to read all files, including sub-folder files from a resources folder, we can use the NIO Files.walk to easily access and read the files.

4.1 The below example uses Files.walk to read all files from a folder src/main/resources/json:

FileResourcesUtils.java

package com.favtuts.io.utils;

import java.io.*;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import java.util.stream.Collectors;

public class FileResourcesUtils {

    public static void main(String[] args) throws IOException {

        FileResourcesUtils app = new FileResourcesUtils();

        // read all files from a resources folder
        try {

            // files from src/main/resources/json
            List<File> result = app.getAllFilesFromResource("json");
            for (File file : result) {
                System.out.println("file : " + file);
                printFile(file);
            }

        } catch (URISyntaxException | IOException e) {
            e.printStackTrace();
        }

    }

    private List<File> getAllFilesFromResource(String folder)
        throws URISyntaxException, IOException {

        ClassLoader classLoader = getClass().getClassLoader();

        URL resource = classLoader.getResource(folder);

        // dun walk the root path, we will walk all the classes
        List<File> collect = Files.walk(Paths.get(resource.toURI()))
                .filter(Files::isRegularFile)
                .map(x -> x.toFile())
                .collect(Collectors.toList());

        return collect;
    }

    // print a file
    private static void printFile(File file) {

        List<String> lines;
        try {
            lines = Files.readAllLines(file.toPath(), StandardCharsets.UTF_8);
            lines.forEach(System.out::println);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

}

Output

file : /home/favtuts/projects/core-java/java-io/target/classes/json/file1.json
{
"name": "favtuts",
"age": 38
}

file : /home/favtuts/projects/core-java/java-io/target/classes/json/file2.json
{
"name": "jack",
"age": 40
}

file : /home/favtuts/projects/core-java/java-io/target/classes/json/sub/subfile1.json
{
"name": "sub",
"age": 99
}

4.2 However, the standard Files.walk in example 4.1 can’t access the files in the JAR file directly, try run the example 4.1 in a JAR environment, and it throws FileSystemNotFoundException.

$ mvn clean package

$ java -jar target/java-io.jar
Exception in thread "main" java.nio.file.FileSystemNotFoundException
        at jdk.zipfs/jdk.nio.zipfs.ZipFileSystemProvider.getFileSystem(ZipFileSystemProvider.java:169)
        at jdk.zipfs/jdk.nio.zipfs.ZipFileSystemProvider.getPath(ZipFileSystemProvider.java:155)
        at java.base/java.nio.file.Path.of(Path.java:208)
        at java.base/java.nio.file.Paths.get(Paths.java:97)
        at com.favtuts.io.utils.FileResourcesUtils.getAllFilesFromResource(FileResourcesUtils.java:128)
        at com.favtuts.io.utils.FileResourcesUtils.main(FileResourcesUtils.java:35)

5. Get all files from a resource folder. (JAR version)

5.1 This example shows how to Files.walk a folder inside a JAR file via FileSystems and URI jar:file:xxx.jar.

The idea is:

  • File walks the folder inside a JAR file using FileSystems, and get all the filename, see getPathsFromResourceJAR()
  • Loop all the filename, access and print each file like example 2.1, see getFileFromResourceAsStream().

FileResourcesUtils.java

package com.favtuts.io.utils;

import java.io.*;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

public class FileResourcesUtils {

    public static void main(String[] args) throws IOException {

        FileResourcesUtils app = new FileResourcesUtils();

        // Sample 3 - read all files from a resources folder (JAR version)
        try {

            // get paths from src/main/resources/json
            List<Path> result = app.getPathsFromResourceJAR("json");
            for (Path path : result) {
                System.out.println("Path : " + path);

                String filePathInJAR = path.toString();
                // Windows will returns /json/file1.json, cut the first /
                // the correct path should be json/file1.json
                if (filePathInJAR.startsWith("/")) {
                    filePathInJAR = filePathInJAR.substring(1, filePathInJAR.length());
                }

                System.out.println("filePathInJAR : " + filePathInJAR);

                // read a file from resource folder
                InputStream is = app.getFileFromResourceAsStream(filePathInJAR);
                printInputStream(is);
            }

        } catch (URISyntaxException | IOException e) {
            e.printStackTrace();
        }

    }

    // get a file from the resources folder
    // works everywhere, IDEA, unit test and JAR file.
    private InputStream getFileFromResourceAsStream(String fileName) {

        // The class loader that loaded the class
        ClassLoader classLoader = getClass().getClassLoader();
        InputStream inputStream = classLoader.getResourceAsStream(fileName);

        // the stream holding the file content
        if (inputStream == null) {
            throw new IllegalArgumentException("file not found! " + fileName);
        } else {
            return inputStream;
        }

    }

    // Get all paths from a folder that inside the JAR file
    private List<Path> getPathsFromResourceJAR(String folder)
        throws URISyntaxException, IOException {

        List<Path> result;

        // get path of the current running JAR
        String jarPath = getClass().getProtectionDomain()
                .getCodeSource()
                .getLocation()
                .toURI()
                .getPath();
        System.out.println("JAR Path :" + jarPath);

        // file walks JAR
        URI uri = URI.create("jar:file:" + jarPath);
        try (FileSystem fs = FileSystems.newFileSystem(uri, Collections.emptyMap())) {
            result = Files.walk(fs.getPath(folder))
                    .filter(Files::isRegularFile)
                    .collect(Collectors.toList());
        }

        return result;

    }

    // print input stream
    private static void printInputStream(InputStream is) {

        try (InputStreamReader streamReader = new InputStreamReader(is, StandardCharsets.UTF_8);
             BufferedReader reader = new BufferedReader(streamReader)) {

            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }

        } catch (IOException e) {
            e.printStackTrace();
        }

    }

}

Output

$ java -jar target/java-io.jar

JAR Path :/C:/Users/favtuts/projects/core-java/java-io/target/java-io.jar

Path : /json/file2.json
filePathInJAR : json/file2.json
{
  "name": "jack",
  "age": 40
}

Path : /json/file1.json
filePathInJAR : json/file1.json
{
  "name": "favtuts",
  "age": 38
}

Path : /json/sub/subfile1.json
filePathInJAR : json/sub/subfile1.json
{
  "name": "sub",
  "age": 99
}

Download Source Code

$ git clone https://github.com/favtuts/java-core-tutorials-examples

$ cd java-io

References

Leave a Reply

Your email address will not be published. Required fields are marked *