Read file from resources folder in Spring Boot
Asked Answered
T

28

223

I'm using Spring Boot and json-schema-validator. I'm trying to read a file called jsonschema.json from the resources folder. I've tried a few different ways but I can't get it to work. This is my code.

ClassLoader classLoader = getClass().getClassLoader();
File file = new File(classLoader.getResource("jsonschema.json").getFile());
JsonNode mySchema = JsonLoader.fromFile(file);

This is the location of the file.

enter image description here

And here I can see the file in the classes folder.

enter image description here

But when I run the code I get the following error.

jsonSchemaValidator error: java.io.FileNotFoundException: /home/user/Dev/Java/Java%20Programs/SystemRoutines/target/classes/jsonschema.json (No such file or directory)

What is it I'm doing wrong in my code?

Transmittance answered 6/6, 2017 at 20:38 Comment(4)
Can you try this? ClassLoader classLoader = getClass().getClassLoader(); JsonNode mySchema = JsonLoader.getJson(classLoader.getResourceAsStream("jsonschema.json"));Hire
I like xubuntu tooArmoured
first two lines of your question were the answer to my problem. you saved my long hours to go! ty @Transmittance ClassLoader classLoader = getClass().getClassLoader(); File file = new File(classLoader.getResource("jsonschema.json").getFile());Prosciutto
why does this question has so many wrong answers?Platinic
D
186

After spending a lot of time trying to resolve this issue, finally found a solution that works. The solution makes use of Spring's ResourceUtils. Should work for json files as well.

Thanks for the well written page by Lokesh Gupta : Blog

enter image description here

package utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ResourceUtils;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import java.io.File;


public class Utils {

    private static final Logger LOGGER = LoggerFactory.getLogger(Utils.class.getName());

    public static Properties fetchProperties(){
        Properties properties = new Properties();
        try {
            File file = ResourceUtils.getFile("classpath:application.properties");
            InputStream in = new FileInputStream(file);
            properties.load(in);
        } catch (IOException e) {
            LOGGER.error(e.getMessage());
        }
        return properties;
    }
}

To answer a few concerns on the comments :

Pretty sure I had this running on Amazon EC2 using java -jar target/image-service-slave-1.0-SNAPSHOT.jar

Look at my github repo : https://github.com/johnsanthosh/image-service to figure out the right way to run this from a JAR.

Dejected answered 24/3, 2018 at 18:22 Comment(7)
Thanks John for adding this. This works and certainly a better approach using the ResourceUtil.Heracliteanism
This will work only if you try to run the application from IDE but when you run the jar it won't find the file for you.Bildungsroman
Agree with Hassan, we should instead use new ClassPathResource("filename").getInputStream() if run the application from jar. DetailHauck
Agree with Hassan. As a caveat, ResourceUtils Javadoc is clear that the class is mainly for internal use. Check as well #25869928Cartwell
@HassanMudassir Pretty sure I had this running on Amazon EC2 using java -jar target/image-service-slave-1.0-SNAPSHOT.jar Look at my github repo : github.com/johnsanthosh/image-service Figure out the right way to package your JAR and this would work 100% ;)Dejected
Hassan is right! I think a fat jar or shadow jar will allow you to use the original method but @JingchaoLuan's method is best.Equivocal
Spring ResourceUtils works for both JAR and IDE after removing the classpath: prefix. :)Billboard
L
83

Very short answer: you are looking for the resource in the scope of a classloader's class instead of your target class. This should work:

File file = new File(getClass().getResource("jsonschema.json").getFile());
JsonNode mySchema = JsonLoader.fromFile(file);

Also, that might be helpful reading:

P.S. there is a case when a project compiled on one machine and after that launched on another or inside Docker. In such a scenario path to your resource folder would be invalid and you would need to get it in runtime:

ClassPathResource res = new ClassPathResource("jsonschema.json");    
File file = new File(res.getPath());
JsonNode mySchema = JsonLoader.fromFile(file);

Update from 2020

On top of that if you want to read resource file as a String, for example in your tests, 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 of usage:

String soapXML = getResourceFileAsString("some_folder_in_resources/SOPA_request.xml");
Limbert answered 6/6, 2017 at 20:45 Comment(7)
getClass().getResource("jsonschema.json") returns null. I also tried ClassPathResource res = new ClassPathResource("jsonschema.json") which just returns jsonschema.json. Does this has something to do with that I'm using Spring Boot?Transmittance
@Transmittance regarding getClass().getResource("jsonschema.json") returns null I could refer to this topic #26328540. On top of that try to rebuild your project. Feedback would be appreciated.Microspore
@Transmittance I provided update on the answer, please checkMicrospore
@povisenko I would suggest you to throw exception if is empty. It means that the file/resource you are looking for is not there.Declass
complete answer. Works both in the IDE and for the jar. Thanks.Backfire
This returns null for me and the answer above only solves the issue.Prefer
It's generally a good practice to close a BufferedReader/InputStreamReader when you are done using it.Dinorahdinosaur
K
55

if you have for example config folder under Resources folder I tried this Class working perfectly hope be useful

File file = ResourceUtils.getFile("classpath:config/sample.txt")

//Read File Content
String content = new String(Files.readAllBytes(file.toPath()));
System.out.println(content);
Kazantzakis answered 31/10, 2018 at 4:3 Comment(1)
I tried your solution, it works in IDE but when you make spring jar input stream will help.Alcazar
T
36

Spent way too much time coming back to this page so just gonna leave this here:

File file = new ClassPathResource("data/data.json").getFile();
Tunstall answered 2/1, 2020 at 19:35 Comment(1)
Works well in IDE but not in JAR-file.Outfall
C
34

2021 The Best Way


Simplest way to read file is:

    Resource resource = new ClassPathResource("jsonSchema.json");
    FileInputStream file = new FileInputStream(resource.getFile());
Creese answered 21/9, 2021 at 13:15 Comment(1)
This won't work in executable jar. Instead we can use InputStream inputStream = resource.getInputStream();Pontine
B
16

How to get resource reliably

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

Example: read file from resources

public class SpringBootResourcesApplication {
    public static void main(String[] args) throws Exception {
        ClassPathResource resource = new ClassPathResource("/hello", SpringBootResourcesApplication.class);
        try (InputStream inputStream = resource.getInputStream()) {
            String string = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
            System.out.println(string);
        }
    }
}
  • ClassPathResource is Spring's implementation of Resource - the abstract way to load resource. It is instantiated using the ClassPathResource(String, Class<?>) constructor:

  • Project structure:

    ├── mvnw
    ├── mvnw.cmd
    ├── pom.xml
    └── src
        └── main
            ├── java
            │   └── com
            │       └── caco3
            │           └── springbootresources
            │               └── SpringBootResourcesApplication.java
            └── resources
                ├── application.properties
                └── hello
    

The example above works from both IDE and jar

Deeper explanation

Prefer abstract resources instead of File
  • Examples of abstract resources are InputStream and URL
  • Avoid using File because it is not always possible to get it from a classpath resource
    • E.g. the following code works in IDE:
    public class SpringBootResourcesApplication {
        public static void main(String[] args) throws Exception {
            ClassLoader classLoader = SpringBootResourcesApplication.class.getClassLoader();
            File file = new File(classLoader.getResource("hello").getFile());
    
            Files.readAllLines(file.toPath(), StandardCharsets.UTF_8)
                    .forEach(System.out::println);
        }
    }
    
    but fails with:
    java.nio.file.NoSuchFileException: file:/home/caco3/IdeaProjects/spring-boot-resources/target/spring-boot-resources-0.0.1-SNAPSHOT.jar!/BOOT-INF/classes!/hello
            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)
    
    when Spring Boot jar run
  • If you use external library, and it asks you for a resource, try to find a way to pass it an InputStream or URL
    • For example the JsonLoader.fromFile from the question could be replaced with JsonLoader.fromURL method: it accepts URL
Use framework's facilities to get the resource:

Spring Framework enables access to classpath resources through ClassPathResource

You can use it:

  1. Directly, as in the example of reading file from resources
  2. Indirectly:
    1. Using @Value:
      @SpringBootApplication
      public class SpringBootResourcesApplication implements ApplicationRunner {
          @Value("classpath:/hello") // Do not use field injection
          private Resource resource;
      
          public static void main(String[] args) throws Exception {
              SpringApplication.run(SpringBootResourcesApplication.class, args);
          }
      
         @Override
         public void run(ApplicationArguments args) throws Exception {
             try (InputStream inputStream = resource.getInputStream()) {
                 String string = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
                 System.out.println(string);
             }
         }
      }
      
    2. Using ResourceLoader:
      @SpringBootApplication
      public class SpringBootResourcesApplication implements ApplicationRunner {
          @Autowired // do not use field injection
          private ResourceLoader resourceLoader;
      
          public static void main(String[] args) throws Exception {
              SpringApplication.run(SpringBootResourcesApplication.class, args);
          }
      
          @Override
          public void run(ApplicationArguments args) throws Exception {
              Resource resource = resourceLoader.getResource("/hello");
              try (InputStream inputStream = resource.getInputStream()) {
                  String string = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
                  System.out.println(string);
              }
          }
      }
      
      • See also this answer
Bifid answered 24/8, 2021 at 20:54 Comment(2)
ClassPathResource does not work in Fat jarAmbala
Can I ask you to provide more details, please, maybe, you can post a simple application where it does not work?Bifid
E
15

See my answer here: https://mcmap.net/q/82436/-spring-boot-can-not-find-resource-file-after-packaging

import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;

Use these 2 imports.

Declare

@Autowired
ResourceLoader resourceLoader;

Use this in some function

Resource resource=resourceLoader.getResource("classpath:preferences.json");

In your case, as you need the file you may use following

File file = resource.getFile()

Reference:http://frugalisminds.com/spring/load-file-classpath-spring-boot/ As already mentioned in previous answers don't use ResourceUtils it doesn't work after deployment of JAR, this will work in IDE as well as after deployment

Emeritaemeritus answered 2/7, 2019 at 14:22 Comment(1)
Which solution? I teseted it and its in PROD, not sure, you must be facing other issue.Emeritaemeritus
H
11

Below is my working code.

List<sampleObject> list = new ArrayList<>();
File file = new ClassPathResource("json/test.json").getFile();
ObjectMapper objectMapper = new ObjectMapper();
sampleObject = Arrays.asList(objectMapper.readValue(file, sampleObject[].class));

Hope it helps one!

Hexa answered 25/2, 2020 at 14:50 Comment(1)
json directory is under Resources of Spring project.Hexa
S
6

stuck in the same issue, this helps me

URL resource = getClass().getClassLoader().getResource("jsonschema.json");
JsonNode jsonNode = JsonLoader.fromURL(resource);
Svetlana answered 20/2, 2018 at 14:48 Comment(1)
actually, this is almost the same as given answer for more details see here #14740050Microspore
D
6

Here is my solution. May help someone;

It returns InputStream, but i assume you can read from it too.

InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("jsonschema.json");
Diluent answered 16/1, 2019 at 20:58 Comment(0)
M
5

create json folder in resources as subfolder then add json file in folder then you can use this code : enter image description here

import com.fasterxml.jackson.core.type.TypeReference;

InputStream is = TypeReference.class.getResourceAsStream("/json/fcmgoogletoken.json");

this works in Docker.

Matless answered 2/6, 2018 at 7:14 Comment(0)
M
4

The simplest method to bring a resource from the classpath in the resources directory parsed into a String is the following one liner.

As a String(Using Spring Libraries):

         String resource = StreamUtils.copyToString(
                new ClassPathResource("resource.json").getInputStream(), defaultCharset());

This method uses the StreamUtils utility and streams the file as an input stream into a String in a concise compact way.

If you want the file as a byte array you can use basic Java File I/O libraries:

As a byte array(Using Java Libraries):

byte[] resource = Files.readAllBytes(Paths.get("/src/test/resources/resource.json"));
Mutualize answered 20/5, 2020 at 18:37 Comment(2)
java.io.FileNotFoundException: class path resource [src/test/resources/html/registration.html] cannot be opened because it does not existBullpup
It's right : InputStream resource = new ClassPathResource( "html/test.html").getInputStream(); String s = StreamUtils.copyToString(resource, StandardCharsets.UTF_8);Bullpup
C
4

Here is a solution with ResourceUtils and Java 11 Files.readString which takes care of UTF-8 encoding and resource closing

import static java.nio.charset.StandardCharsets.UTF_8;
import static org.springframework.util.FileCopyUtils.copyToByteArray;
import org.springframework.core.io.ClassPathResource;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

public  JsonNode getJsonData() throws IOException {
  ClassPathResource classPathResource = new 
  ClassPathResource("assets/data.json");
        byte[] byteArray = 
  copyToByteArray(classPathResource.getInputStream());
        return new ObjectMapper() //
                .readTree(new String(byteArray, UTF_8));
}

Or even simpler

Step 1 : Create your resource file lets say under /src/main/resources/data/test.data
Step 2 : Define the value in application.properties/yml

com.test.package.data=#{new org.springframework.core.io.ClassPathResource("/data/test.data").getFile().getAbsolutePath()}

Step 3 : Get the file in your code

@Value("${com.test.package.data}")
private String dataFile;

private void readResourceFile() {
   Path path = Paths.get(dataFile);
   List<String> allLines = Files.readAllLines(path);
}
Crampton answered 7/4, 2022 at 12:13 Comment(0)
J
3

If you're using spring and jackson (most of the larger applications will), then use a simple oneliner:

JsonNode json = new ObjectMapper().readTree(new ClassPathResource("filename").getFile());

Jorgenson answered 4/3, 2021 at 8:25 Comment(0)
R
3

Spring provides ResourceLoader which can be used to load files.

@Autowired
ResourceLoader resourceLoader;


// path could be anything under resources directory
File loadDirectory(String path){
        Resource resource = resourceLoader.getResource("classpath:"+path); 
        try {
            return resource.getFile();
        } catch (IOException e) {
            log.warn("Issue with loading path {} as file", path);
        }
        return null;
 }

Referred to this link.

Remunerative answered 7/4, 2021 at 12:57 Comment(0)
A
2

just to add my solution as another 2 cents together with all other answers. I am using the Spring DefaultResourceLoader to get a ResourceLoader. Then the Spring FileCopyUtils to get the content of the resource file to a string.

import static java.nio.charset.StandardCharsets.UTF_8;

import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.UncheckedIOException;

import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.util.FileCopyUtils;

public class ResourceReader {
    public static String readResourceFile(String path) {
        ResourceLoader resourceLoader = new DefaultResourceLoader();
        Resource resource = resourceLoader.getResource(path);
        return asString(resource);
    }

    private static String asString(Resource resource) {
        try (Reader reader = new InputStreamReader(resource.getInputStream(), UTF_8)) {
            return FileCopyUtils.copyToString(reader);
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }
}
Aurea answered 6/4, 2022 at 13:4 Comment(0)
W
2

Short and Simple:

Works both in IDE and Jar. If your file is under resource folder:

InputStream inputStream = getClass().getResourceAsStream("/file.txt");

If your file is under resource/folder then simply change to:

InputStream inputStream = getClass().getResourceAsStream("/folder/file.txt");

Note: Above snippet works in non-static methods

For static methods use the prefix YourClass.class.getClass()

Willett answered 19/7, 2023 at 7:17 Comment(0)
S
1

For me, the bug had two fixes.

  1. Xml file which was named as SAMPLE.XML which was causing even the below solution to fail when deployed to aws ec2. The fix was to rename it to new_sample.xml and apply the solution given below.
  2. Solution approach https://medium.com/@jonathan.henrique.smtp/reading-files-in-resource-path-from-jar-artifact-459ce00d2130

I was using Spring boot as jar and deployed to aws ec2 Java variant of the solution is as below :

package com.test;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.Resource;


public class XmlReader {

    private static Logger LOGGER = LoggerFactory.getLogger(XmlReader.class);

  public static void main(String[] args) {


      String fileLocation = "classpath:cbs_response.xml";
      String reponseXML = null;
      try (ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext()){

        Resource resource = appContext.getResource(fileLocation);
        if (resource.isReadable()) {
          BufferedReader reader =
              new BufferedReader(new InputStreamReader(resource.getInputStream()));
          Stream<String> lines = reader.lines();
          reponseXML = lines.collect(Collectors.joining("\n"));

        }      
      } catch (IOException e) {
        LOGGER.error(e.getMessage(), e);
      }
  }
}
Swingeing answered 3/3, 2020 at 14:22 Comment(0)
M
1

If you are using maven resource filter in your proyect, you need to configure what kind of file is going to be loaded in pom.xml. If you don't, no matter what class you choose to load the resource, it won't be found.

pom.xml

<resources>
    <resource>
        <directory>${project.basedir}/src/main/resources</directory>
        <filtering>true</filtering>
        <includes>
            <include>**/*.properties</include>
            <include>**/*.yml</include>
            <include>**/*.yaml</include>
            <include>**/*.json</include>
        </includes>
    </resource>
</resources>
Mcneil answered 29/4, 2021 at 15:42 Comment(0)
H
1

Below works in both IDE and running it as a jar in the terminal,

import org.springframework.core.io.Resource;

@Value("classpath:jsonschema.json")
Resource schemaFile;
    
JsonSchemaFactory factory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4);
JsonSchema jsonSchema = factory.getSchema(schemaFile.getInputStream());
Hogle answered 5/5, 2021 at 1:22 Comment(0)
H
1

You need to sanitize the path and replace %20 with a space, or rename your directory. Then it should work.

FileNotFoundException: /home/user/Dev/Java/Java%20Programs/SystemRoutines/target/classes/jsonschema.json
Hardecanute answered 6/4, 2022 at 9:40 Comment(0)
L
0

i think the problem lies within the space in the folder-name where your project is placed. /home/user/Dev/Java/Java%20Programs/SystemRoutines/target/classes/jsonschema.json

there is space between Java Programs.Renaming the folder name should make it work

Lindsy answered 9/9, 2020 at 5:34 Comment(0)
A
0

Using Spring ResourceUtils.getFile() you don't have to take care absolute path :)

 private String readDictionaryAsJson(String filename) throws IOException {
    String fileContent;
    try {
        File file = ResourceUtils.getFile("classpath:" + filename);
        Path path = file.toPath();
        Stream<String> lines = Files.lines(path);
        fileContent = lines.collect(Collectors.joining("\n"));
    } catch (IOException ex) {
        throw ex;
    }
    return new fileContent;
}
Antifebrile answered 29/7, 2021 at 16:44 Comment(0)
R
0

Try this:

In application.properties

app.jsonSchema=classpath:jsonschema.json

On your Properties pojo:

NOTE: You can use any prefered way of reading configs from application.properties.

@Configuration
@ConfigurationProperties(prefix = "app") 
public class ConfigProperties {
private Resource jsonSchema;

// standard getters and setters
}

In your class, read the resource from the Properties Pojo:

//Read the Resource and get the Input Stream
try (InputStream inStream = configProperties.getJsonSchema().getInputStream()) {
   //From here you can manipulate the Input Stream as desired....
   //Map the Input Stream to a Map
    ObjectMapper mapper = new ObjectMapper();
    Map <String, Object> jsonMap = mapper.readValue(inStream, Map.class);
    //Convert the Map to a JSON obj
    JSONObject json = new JSONObject(jsonMap);
    } catch (Exception e) {
        e.printStackTrace();
    }
Rarely answered 17/9, 2021 at 15:50 Comment(0)
A
0

Nowadays, in 2023, Java users should be able to read a classpath file more easily. With a simple instruction such as new File("classpath:path-to-file").

Adeline answered 4/2, 2023 at 4:32 Comment(0)
M
0

The easiest way to do it using PathMatchingResourcePatternResolver,

PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();

Resource[] resources = resolver.getResources("classpath:*.json");

for (Resource resource: resources) {
    JsonNode mySchema = JsonLoader.fromFile(resource.getFile());
}
Matchlock answered 8/5, 2023 at 11:51 Comment(0)
H
-1

I had same issue and because I just had to get file path to send to file input stream, I did this way.

    String pfxCertificate ="src/main/resources/cert/filename.pfx";
    String pfxPassword = "1234";
    FileInputStream fileInputStream = new FileInputStream(pfxCertificate));
Helio answered 29/6, 2021 at 5:3 Comment(0)
B
-1
 private void managerWriteFileToClasspath() throws IOException {
        //get absolute path
        File file = ResourceUtils.getFile("classpath:application.yml");
        //get path to directory when is application.yml
        String parent = file.getParent();

        String separator = File.separator; //separator for various OS

        //create directory into src/main/resources.
        // You will see in target/classes
        String directoryPath = parent + separator + "data";
        Path newFilePath = Paths.get(directoryPath);
        Files.createDirectory(newFilePath);

        //create file into directory src/main/resources/data
        String fileTxt = parent + separator + "data" + separator + "example.txt";
        String str = "Hello hello";
        try(BufferedWriter writer = new BufferedWriter(new FileWriter(fileTxt))){
            writer.write(str);
        }

        //read file in classpath (src/main/resources/data)
        String fileInClasspath = readFileInClasspath("data/example.txt");
        System.out.println(fileInClasspath);
    }


    private String readFileInClasspath(String filename) throws IOException {
        String fileContent ;
        try {
            File file = ResourceUtils.getFile("classpath:" + filename);
            Path path = file.toPath();
            Stream<String> lines = Files.lines(path);
            fileContent = lines.collect(Collectors.joining("\n"));
        } catch (IOException ex) {
            throw ex;
        }

        return  fileContent;
    }

Bullpup answered 28/2, 2023 at 19:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.