Java IO - Read file from resources folder reliably in Spring Boot

Read file from resources folder reliable in Spring Boot

Sample implementation: https://github.com/explorer436/programming-playground/tree/main/java-playground/handling-backend-lookups-examples

To reliably get a file from the resources in Spring Boot application:

  1. Find a way to pass abstract resource, for example, InputStream, URL instead of File
  2. Use framework facilities to get the resource

Avoid using File because it is not always possible to get it from a classpath resource When we want to read a file from the resources folder in spring boot, we need to use resource.getInputStream() (and not resource.getFile())

mvn clean package && java -jar target/spring-caching-data-from-resource-files-demo-0.0.1-SNAPSHOT.jar

Both of these will work when we are running the application in an IDE. But when we package the jar file and run it from terminal, resource.getFile() will not work. In that scenario, if we try to read it as a file, we will run into this error: Classpath resource not found when running as jar

java.io.FileNotFoundException: class path resource [IdValuesFile.json] cannot be resolved to absolute file path because it does not reside in the file system: jar:file:/home/explorer436/Downloads/GitRepositories/programming-playground/java-playground/spring-caching-data-from-resource-files-demo/target/spring-caching-data-from-resource-files-demo-0.0.1-SNAPSHOT.jar!/BOOT-INF/classes!/IdValuesFile.json
        at org.springframework.util.ResourceUtils.getFile(ResourceUtils.java:217) ~[spring-core-6.0.11.jar!/:6.0.11]

resource.getFile() expects the resource itself to be available on the file system, i.e. it can’t be nested inside a jar file. This is why it works when you run your application in IDE but doesn’t work once you’ve built your application and run it from the executable jar. Rather than using getFile() to access the resource’s contents, use getInputStream() instead. That’ll allow you to read the resource’s content regardless of where it’s located.

Case sensitivity of the file names

Another important thing to note: When running the application it ignores capitals in file/folders in the resources folder where it doesn’t ignore it while running as a jar.

@Autowired
ApplicationContext appContext;

// this will work when running the application, but will fail when running as jar
appContext.getResource("classpath:testfolder/message.txt");

Therefore, don’t use capitals in your resources or also add those capitals in your constructor of ClassPathResource:

appContext.getResource("classpath:Testfolder/message.txt");

A couple of utility methods

If you want to read resource file as a String, you can use these static utils methods:

public static String getResourceFileAsString(String fileName) {
    InputStream is = getResourceFileAsInputStream(fileName);
    if (is != null) {
        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
        return (String)reader.lines().collect(Collectors.joining(System.lineSeparator()));
    } else {
        throw new RuntimeException("resource not found");
    }
}

public static InputStream getResourceFileAsInputStream(String fileName) {
    ClassLoader classLoader = {CurrentClass}.class.getClassLoader();
    return classLoader.getResourceAsStream(fileName);
}

Example usage:

String soapXML = getResourceFileAsString("some_folder_in_resources/SOPA_request.xml");

Links to this note