在Spring Boot中从资源文件夹读取文件

207

我正在使用Spring Boot和json-schema-validator。我试图从resources文件夹中读取一个名为jsonschema.json的文件。我尝试了几种不同的方法,但无法使其正常工作。这是我的代码。

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

这是文件的位置。

enter image description here

在这里,我可以看到classes文件夹中的文件。

enter image description here

但是当我运行代码时,会出现以下错误。

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

我在代码中做错了什么?


1
你能试试这个吗?ClassLoader classLoader = getClass().getClassLoader(); JsonNode mySchema = JsonLoader.getJson(classLoader.getResourceAsStream("jsonschema.json")); - harshavmb
1
我也喜欢 Xubuntu。 - Dmitry P.
你的问题的答案就在你提问的前两行。谢谢 @g3blv,你帮我节省了很多时间! ClassLoader classLoader = getClass().getClassLoader(); File file = new File(classLoader.getResource("jsonschema.json").getFile()); - Jayant
为什么这个问题有这么多错误的答案? - ACV
28个回答

177

在花费了很多时间尝试解决这个问题之后,终于找到了一个可行的解决方案。该解决方案利用了Spring的ResourceUtils工具。对于json文件也应该适用。

感谢Lokesh Gupta编写的博客页面

输入图像描述

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;
    }
}
回答一下评论中的几个问题:
我很确定我在Amazon EC2上使用了java -jar target/image-service-slave-1.0-SNAPSHOT.jar来运行它。
查看我的GitHub仓库:https://github.com/johnsanthosh/image-service 以找出从JAR文件运行此代码的正确方法。

2
@Athar 很高兴我能帮到你。 - John
72
只有在尝试从IDE运行应用程序时,此方法才能正常工作,但当您运行JAR文件时,它将无法为您找到该文件。 - Hassan Mudassir
34
同意Hassan的看法,如果从jar包中运行应用程序,我们应该改用new ClassPathResource("filename").getInputStream()来访问文件。详见链接 - Jingchao Luan
3
同意Hassan的观点。需要注意的是,ResourceUtils Javadoc明确指出该类主要用于内部使用。同时请查看https://dev59.com/GF8e5IYBdhLWcg3wTJGL#25873705。 - Eric Jiang
1
哈桑是正确的!我认为一个fat jar或shadow jar将允许您使用原始方法,但@JingchaoLuan的方法是最好的。 - JackMahoney
显示剩余4条评论

80

简短回答:您正在查找类加载器类的范围内的资源,而不是您的目标类。这应该可以解决问题:

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

此外,阅读以下内容可能会有所帮助:

另外,在一个项目在一台机器上编译后,在另一台机器或Docker内部启动时可能会遇到路径指向资源文件夹无效的情况,这种情况下您需要在运行时获取它:

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

2020年更新

此外,如果您想将资源文件作为字符串读取,例如在测试中,可以使用这些静态实用程序方法:

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

使用示例:

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

4
getClass().getResource("jsonschema.json") 返回 null。我还尝试了 ClassPathResource res = new ClassPathResource("jsonschema.json"),它只返回了 jsonschema.json。这是否与我使用的 Spring Boot 有关系? - g3blv
@g3blv 关于 getClass().getResource("jsonschema.json") 返回 null,我可以参考这个主题 https://dev59.com/YV8d5IYBdhLWcg3w41gp#40065607。此外,请尝试重新构建您的项目。期待您的反馈。 - Serhii Povísenko
1
@povisenko 我建议你在 is 为空时抛出异常。这意味着你正在寻找的文件/资源不存在。 - endertunc
1
完整答案:可以在IDE和jar中都运行。谢谢。 - Digao
这对我来说返回null,而上面的答案只解决了问题。 - Arefe
显示剩余2条评论

52

如果您在Resources文件夹下有一个config文件夹, 我尝试过这个类,工作得非常完美,希望对你有用

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

//Read File Content
String content = new String(Files.readAllBytes(file.toPath()));
System.out.println(content);

17
我尝试了你的解决方案,在IDE中可以工作,但是当你制作Spring JAR文件时,需要使用输入流。 - Ameya

34

浪费了太多时间回到这个页面,所以我要把它留在这里:

File file = new ClassPathResource("data/data.json").getFile();

5
在IDE中运行良好,但在JAR文件中无法正常工作。 - Kevin O.

25

2021年最佳方法


读取文件的最简单方法是:

    Resource resource = new ClassPathResource("jsonSchema.json");
    FileInputStream file = new FileInputStream(resource.getFile());

20
这种方法在可执行的JAR文件中不起作用。相反,我们可以使用 InputStream inputStream = resource.getInputStream(); - srp

14

请看我在这里的回答:https://dev59.com/87Dma4cB1Zd3GeqPDeml#56854431

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

使用以下两个导入。

声明

@Autowired
ResourceLoader resourceLoader;

在某个函数中使用此代码

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

在您的情况下,如果您需要该文件,可以使用以下代码:

File file = resource.getFile()

参考:http://frugalisminds.com/spring/load-file-classpath-spring-boot/ 如前面的回答中已经提到的,请不要使用ResourceUtils,因为它在JAR部署后无法正常工作,而上述代码可以在IDE以及部署后都正常运行。


哪个解决方案?我测试过了,它已经在生产环境中了。不确定,你可能遇到了其他问题。 - sajal rajabhoj

13

如何可靠地获取资源

为了在Spring Boot应用程序中可靠地获取资源文件:

  1. 找到传递抽象资源的方法,例如使用 InputStreamURL等,而不是 File
  2. 使用框架提供的工具来获取资源。

示例:从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);
        }
    }
}

上面的示例适用于IDE和jar

更深入的解释

优先使用抽象资源而不是File
  • 抽象资源的示例包括InputStreamURL
  • 避免使用File,因为不能始终从类路径资源中获取它。
    • 例如,以下代码可以在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);
        }
    }
    
    但是出现了以下错误:
    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)
    
    当使用Spring Boot运行jar时,如果您使用外部库并且它要求您提供资源,请尝试找到传递给它输入流或URL的方法。例如,在问题中的JsonLoader.fromFile可以替换为JsonLoader.fromURL方法:它接受URL。使用框架的工具来获取资源:Spring Framework通过ClassPathResource实现了访问类路径资源的功能。您可以直接使用它读取resources文件夹中的文件,也可以间接使用@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);
           }
       }
    }
    
    使用 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);
            }
        }
    }
    
    • 参见回答。

ClassPathResource在Fat jar中无法工作。 - user12384512
可以请您提供更多细节吗?也许您可以发布一个简单的应用程序,以便我们查看它为何无法正常工作? - Denis Zavedeev

11

以下是我的工作代码。

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

希望对某人有所帮助!


JSON目录位于Spring项目的Resources文件夹下。 - Bhaumik Thakkar

6

我遇到了同样的问题,这篇文章对我很有帮助。

URL resource = getClass().getClassLoader().getResource("jsonschema.json");
JsonNode jsonNode = JsonLoader.fromURL(resource);

实际上,这几乎与给出的答案相同。 更多细节请参见此处 https://dev59.com/pWUq5IYBdhLWcg3wKtNL - Serhii Povísenko

6
这是我的解决方案。可能会对某些人有所帮助; 它返回InputStream,但我认为你也可以从中读取。
InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("jsonschema.json");

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接