getResourceAsStream返回null

我正在从我的Java项目的编译JAR中的包中加载一个文本文件。相关目录结构如下:

/src/initialization/Lifepaths.txt

我的代码通过调用Class::getResourceAsStream来返回InputStream来加载文件。

public class Lifepaths {
public static void execute() {
System.out.println(Lifepaths.class.getClass().
getResourceAsStream("/initialization/Lifepaths.txt"));
}


private Lifepaths() {}


//This is temporary; will eventually be called from outside
public static void main(String[] args) {execute();}
}

打印输出将始终打印null,无论我使用什么。我不知道为什么上面的方法不管用,所以我也尝试了一下:

  • "/src/initialization/Lifepaths.txt"
  • "initialization/Lifepaths.txt"
  • "Lifepaths.txt"

这两种方法都不起作用。 众多 问题到目前为止在这个主题上,但没有一个是有帮助的-通常,他们只是说使用根路径加载文件,我已经在做了。或者只是从当前目录加载文件(只是加载filename),我也尝试过。该文件将被编译到JAR中的适当位置,并具有适当的名称。

我怎么解决这个问题?

436072 次浏览

规则如下:

  1. 检查您想要在JAR中加载的文件的位置(因此也要确保它确实添加到了JAR中)
  2. 使用绝对路径:路径从JAR的根开始
  3. 使用相对路径:路径开始于你调用getResource/ getResoucreAsStream类的包目录

试一试:

Lifepaths.class.getResourceAsStream("/initialization/Lifepaths.txt")

而不是

Lifepaths.class.getClass().getResourceAsStream("/initialization/Lifepaths.txt")

(不确定是否有区别,但前者将使用正确的ClassLoader/ JAR,而我不确定后者)

Lifepaths.class.getClass().getResourceAsStream(...)使用系统类加载器加载资源,它显然失败了,因为它看不到你的jar

Lifepaths.class.getResourceAsStream(...)使用与加载Lifepaths类相同的类加载器加载资源,它应该可以访问jar中的资源

因此,从jar中获取资源有几种方法,每种方法的语法略有不同,需要指定不同的路径。

我所见过的最好的解释是来自信息世界的这篇文章。我在这里总结一下,但如果你想了解更多,你应该看看这篇文章。

方法

  1. ClassLoader.getResourceAsStream()

格式:"/"-分开的名称;没有前导"/"(所有名字都是绝对的)。

例如:this.getClass().getClassLoader().getResourceAsStream("some/pkg/resource.properties");

  1. Class.getResourceAsStream()

格式:"/"-分开的名称;领先的“/”;表示绝对名称;所有其他名称都是相对于类的包

例如:this.getClass().getResourceAsStream("/some/pkg/resource.properties");

2020年9月更新:更改文章链接。原文来自Javaworld,它现在托管在InfoWorld上(并且有更多广告)

你可能想尝试这个来获得流,即首先获得url,然后将其作为流打开。

URL url = getClass().getResource("/initialization/Lifepaths.txt");
InputStream strm = url.openStream();

我曾经有一个类似的问题:从jar中读取txt文件失败,但读取图像正常

@Emracool……我建议你换个选择。因为你似乎要加载一个*.txt文件。最好使用FileInputStream()而不是恼人的getClass().getClassLoader().getResourceAsStream()getClass().getResourceAsStream()。至少您的代码将正确执行。

不要使用绝对路径,让它们相对于项目中的“资源”目录。快速和肮脏的代码,显示MyTest.txt的内容从目录“资源”。

@Test
public void testDefaultResource() {
// can we see default resources
BufferedInputStream result = (BufferedInputStream)
Config.class.getClassLoader().getResourceAsStream("MyTest.txt");
byte [] b = new byte[256];
int val = 0;
String txt = null;
do {
try {
val = result.read(b);
if (val > 0) {
txt += new String(b, 0, val);
}
} catch (IOException e) {
e.printStackTrace();
}
} while (val > -1);
System.out.println(txt);
}

不知道是否有帮助,但在我的情况下,我的资源在/src/文件夹中,并得到这个错误。 然后我将图片移动到bin文件夹,它修复了这个问题

确保您的资源目录(例如:"src")在类路径中(确保它是eclipse中构建路径中的源目录)。

确保clazz是从主类加载器加载的。

然后,加载src/initialization/Lifepaths.txt,使用

clazz.getResourceAsStream("/initialization/Lifepaths.txt");
< p >原因: clazz.getResourcesAsStream(foo)在clazz的类路径中相对于克拉兹所在的目录中查找foo。前导的“/”使它从clazz类路径中的任何目录的根目录加载

除非您在某种类型的容器中,比如Tomcat,或者直接使用classloader做一些事情,否则您可以只将eclipse/命令行类路径视为唯一的类加载器类路径。

你正在使用的ClassLoader似乎有问题。使用contextClassLoader加载类。这与它是静态方法还是非静态方法无关

Thread.currentThread().getContextClassLoader().getResourceAsStream……

对我有用的是在My Project/Java Resources/src下添加文件,然后使用

this.getClass().getClassLoader().getResourceAsStream("myfile.txt");

我不需要显式地将此文件添加到路径中(将其添加到/src显然会这样做)

我发现自己也遇到了类似的问题。因为我使用maven,我需要更新我的pom.xml,包括这样的东西:

   ...
</dependencies>
<build>
<resources>
<resource>
<directory>/src/main/resources</directory>
</resource>
<resource>
<directory>../src/main/resources</directory>
</resource>
</resources>
<pluginManagement>
...

请注意其中的资源标记,以指定文件夹的位置。如果你有嵌套的项目(像我一样),那么你可能想从其他领域获得资源,而不仅仅是在你工作的模块中。如果您使用类似的配置数据,这有助于减少在每次回购中保持相同的文件

默认的JVM类加载器将首先使用父类加载器加载资源: deletegate-parent-classloader.

.

Lifepaths.class.getClass()的类加载器是bootstrap classloader,所以getResourceAsStream只搜索$JAVA_HOME,不管用户提供了classpath。显然,Lifepaths.txt不在那里。

Lifepaths.class的类加载器是system classpath classloader,因此getResourceAsStream将搜索用户定义的classpath,而Lifepaths.txt在那里。

当使用java.lang.Class#getResourceAsStream(String name)时,不以' / '开头的名称将以package name作为前缀添加。为了避免这种情况,可以使用java.lang.ClassLoader#getResourceAsStream

例如:

ClassLoader loader = Thread.currentThread().getContextClassLoader();
String resourceName = "Lifepaths.txt";
InputStream resourceStream = loader.getResourceAsStream(resourceName);

粗略地说:

__abc0 ~= __abc1

假设你的项目结构如下:

├── src
│   ├── main
│   └── test
│       ├── java
│       │   └── com
│       │       └── github
│       │           └── xyz
│       │               └── proj
│       │                   ├── MainTest.java
│       │                   └── TestBase.java
│       └── resources
│           └── abcd.txt
└── target
└── test-classes  <-- this.getClass.getResource("/")
│              `--Thread.currentThread().getContextClassLoader().getResources(".")
├── com
        │   └── github
│       └── xyz
│           └── proj  <-- this.getClass.getResource(".")
│               ├── MainTest.class
│               └── TestBase.class
└── resources
└── abcd.txt


// in MainTest.java
this.getClass.getResource("/") -> "~/proj_dir/target/test-classes/"
this.getClass.getResource(".") -> "~/proj_dir/target/test-classes/com/github/xyz/proj/"
Thread.currentThread().getContextClassLoader().getResources(".") -> "~/proj_dir/target/test-classes/"
Thread.currentThread().getContextClassLoader().getResources("/") ->  null


如果您使用Maven,请确保您的包装是“jar”而不是“pom”。

<packaging>jar</packaging>

您真正需要的是文件的一个完整的绝对classPath。因此,与其猜测,不如尝试找出ROOT,然后将文件移动到以1 <为基数的更好的位置。战争>文件结构…

URL test1 = getClass().getResource("/");
URL test2 = getClass().getClassLoader().getResource("/");
URL test3 = getClass().getClassLoader().getResource("../");


logger.info(test1.getPath());
logger.info(test2.getPath());
logger.info(test3.getPath());

对我有用的是我把文件放在下面

src/main/java/myfile.log

而且

InputStream is = getClass().getClassLoader().getResourceAsStream("myfile.log");
        

if (is == null) {
throw new FileNotFoundException("Log file not provided");
}

请删除

. . / src / main /资源

或者包含您要读取的文件

在pom.xml中管理或删除 . . / src / main /资源

我的调用者类在src/main/...

是什么帮助我从src/test/resources/folder/file.properties加载资源

`properties.load(getClass().getClassLoader().getResourceAsStream("folder/file.properties"));`

https://howtodoinjava.com/java/io/read-file-from-resources-folder/

Java 11

JDK >=9需要在module-info模块中把非根目录/非共父目录的包给它open了。

JDK >=9需要在module-info.java中打开资源目录,例如:

    <李> src
    • java
    • <李> resoureces
        <李>相依
        • json
      • log4j2.xml
      • openapi.yaml

如果需要读取conf/config。Json,你需要两个步骤:

// in module-info.java
module your.mod.name {
open conf;
}


// then in java code
getClassLoader().getResourceAsStream("conf/config.json");

否则,如果你需要在根目录下读取other,它只是:

getClassLoader().getResourceAsStream("openapi.yaml");

你可以看到{@link java.lang.ClassLoader#getResourceAsStream(String)}知道为什么~

虽然不是一个答案,但我把这个建议作为一个答案,这样我就可以把截图放进去。

对于那些在Windows平台上开发的人,我强烈推荐Microsoft实用程序ProcMon,或进程监视器。

Process Monitor是微软提供的一套实用程序中的一个实用程序(甚至不需要登录就可以下载)。它们都可以在sysinternals.com上获得,sysinternals.com是微软收购并保留的原始主机。

进程监视器可以监视与任何正在运行的进程相关的大量事件。如果指定File Open事件并提供文件名(或文件名的一部分),它将记录哪个进程试图打开该文件,并显示搜索的每个目录。当您不知道正在运行的代码在哪里寻找您在源/配置中指定的文件时,这可能很有用。 下面是一个例子:

enter image description here

这里我正在寻找(并没有找到)一个模式文件(XSD),以便代码可以使用它来验证一些用户提供的XML。

如果您正在使用IDEA,请确保将您的资源文件夹标记为“resources”,以便IDE可以正确识别该路径。

假设你有一个名为book的文件。Json在resources/中,然后使用其中的资源,你可以这样做

InputStream input = Main.class.getResourceAsStream("/book.json");

enter image description here

< p > Lifepaths.class.getClass()。 getResourceAsStream(“Lifepaths.txt")); < / p >
我知道这里有不同的答案。我的工作是这样的。 资源目录下新增文件。 在可执行代码中,我用"/"作为文件名引号中的前缀
 InputStream resourceStream = loader.getResourceAsStream("/LifePaths.txt");

我的问题是资源不在src文件夹。