如何在加载资源时避免Java FileNotFoundException

2023/05/29

1. 概述

在本教程中,我们将探讨在Java应用程序中读取资源文件时可能出现的问题:在运行时,资源文件夹很少与我们的源代码位于磁盘上的同一位置。

让我们看看Java如何让我们在代码打包后访问资源文件。

2. 读取文件

假设我们的应用程序在启动期间读取一个文件:

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

如果我们在IDE中运行上述代码,文件加载时不会出现错误。这是因为我们的IDE使用我们的项目目录作为其当前工作目录,而src/main/resources目录就在那里供应用程序读取。

现在假设我们使用Maven JAR插件将代码打包为JAR。

当我们在命令行运行它时:

java -jar core-java-io2.jar

我们会看到以下错误:

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 cn.tuyucheng.taketoday.resource.MyResourceLoader.loadResourceWithReader(MyResourceLoader.java:14)
	at cn.tuyucheng.taketoday.resource.MyResourceLoader.main(MyResourceLoader.java:37)

3. 源代码与编译代码

当我们构建JAR时,资源被放置在打包工件的根目录中。

在我们的示例中,我们看到源代码设置在源代码目录的src/main/resources中有input.txt。

然而,在相应的JAR结构中,我们看到:

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

此处,input.txt位于JAR的根目录中。因此,当代码执行时,我们将看到FileNotFoundException。

即使我们将路径更改为/input.txt,原始代码也无法加载此文件,因为资源通常无法作为磁盘上的文件进行寻址。资源文件打包在JAR中,因此我们需要一种不同的方式来访问它们。

4. Resources

让我们改为使用资源加载从类路径而不是特定文件位置加载资源。无论代码如何打包,这都将起作用:

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()查看给定资源的类路径。getResourceAsStream()输入中的前导斜杠告诉加载器从类路径的基址读取。我们的JAR文件的内容在classpath中,所以这个方法有效。

IDE通常在其类路径中包含src/main/resources,从而找到这些文件

5. 总结

在这篇简短的文章中,我们将加载文件作为类路径资源来实现,以使我们的代码无论以何种方式打包都能够始终如一地工作。

与往常一样,本教程的完整源代码可在GitHub上获得。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章