How to Avoid the Java FileNotFoundException When Loading Resources – 如何避免加载资源时出现的Java FileNotFoundException

最后修改: 2019年 9月 6日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this tutorial, we’ll explore an issue that can come up when reading resource files in a Java application: At runtime, the resource folder is seldom in the same location on disk as it is in our source code.

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

Let’s see how Java allows us to access resource files after our code has been packaged.

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

2. Reading Files

2.阅读文件

Let’s say our application reads a file during startup:

假设我们的应用程序在启动时读取了一个文件。

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

If we run the above code in an IDE, the file loads without an error. This is because our IDE uses our project directory as its current working directory and the src/main/resources directory is right there for the application to read.

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

Now let’s say we use the Maven JAR plugin to package our code as a JAR.

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

When we run it at the command line:

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

java -jar core-java-io2.jar

We’ll see the following error:

我们会看到以下错误。

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

3. Source Code vs Compiled Code

3.源代码与编译后的代码

When we build a JAR, the resources get placed into the root directory of the packaged artifacts.

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

In our example, we see the source code setup has input.txt in src/main/resources in our source code directory.

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

In the corresponding JAR structure, however, we see:

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

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

Here, input.txt is at the root directory of the JAR. So when the code executes, we’ll see the FileNotFoundException.

这里,input.txt在JAR的根目录下。所以当代码执行时,我们会看到FileNotFoundException

Even if we changed the path to /input.txt the original code could not load this file as resources are not usually addressable as files on disk. The resource files are packaged inside the JAR and so we need a different way of accessing them.

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

4. Resources

4.资源

Let’s instead use resource loading to load resources from the classpath instead of a specific file location. This will work regardless of how the code is packaged:

让我们改用资源加载来从classpath加载资源,而不是特定的文件位置。无论代码是如何打包的,这都会起作用。

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() looks at the classpath for the given resource. The leading slash on the input to getResourceAsStream() tells the loader to read from the base of the classpath. The contents of our JAR file are on the classpath, so this method works.

ClassLoader.getResourceAsStream()查看给定资源的classpath。getResourceAsStream()输入的前导斜线告诉加载器从classpath的底部读取。我们的JAR文件的内容就在classpath上,所以这个方法是可行的。

An IDE typically includes src/main/resources on its classpath and, thus, finds the files.

一个IDE通常在其classpath上包括src/main/resources,因此,可以找到这些文件。

5. Conclusion

5.总结

In this quick article, we implemented loading files as classpath resources, to allow our code to work consistently regardless of how it was packaged.

在这篇快速文章中,我们实现了将文件作为classpath资源加载,以使我们的代码能够一致地工作,无论它是如何被打包的。

As always, the example code is available over on GitHub.

像往常一样,示例代码可在GitHub上获得