ChatGPT解决这个技术问题 Extra ChatGPT

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”

“初始化/Lifepaths.txt”

“生命路径.txt”

这些都不起作用。 I've read numerous questions 到目前为止,它们都没有帮助 - 通常,他们只是说使用根路径加载文件,我已经在这样做了。那,或者只是从当前目录加载文件(只需加载 filename),我也尝试过。该文件正在以适当的名称编译到适当位置的 JAR 中。

我该如何解决这个问题?

你检查过它真的在jar文件中吗?你检查过文件大小写吗?
@JonSkeet 确实是在合适的位置编译成JAR文件,大小写正确。
@greedybuddha 虽然我不能从静态上下文中调用它,但我可以使用 Lifepaths.class 调用它。话虽如此,为什么 getClassLoader() 允许它工作? (另外,请随时发布答案!)
你能显示Lifepaths.getClass()吗? Object 中没有定义这样的静态方法...
看看 this answer &看看你是否可以使用 getResource(String) 让它工作。顺便说一句 - 我一直无法让其中任何一个在 static 上下文中工作。问题基本上是获得的类加载器是用于 J2SE 类的。您需要访问适用于应用程序本身的 context 类加载器。

h
hoaz

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

Lifepaths.class.getResourceAsStream(...) 使用加载 Lifepaths 类的同一个类加载器加载资源,并且它应该可以访问您的 JAR 中的资源


补充一点,当调用 getResourceAsStream(name) 时,名称必须以“/”开头。我不确定这是否有必要,但没有它我有问题。
从今天/昨天早上 8 点开始,我一直在搞砸这个。救了我。我确实还需要一个前导斜杠才能使其工作。
还要记住,所需的源可以在包层次结构之外。在这种情况下,您必须在路径中使用“../”来上一层,然后再下到另一个路径分支以访问您的资源。
@David——我认为它(前导'/')是必要的,否则它会相对于 Lifepaths.class 包进行搜索
只是为了添加一些信息,如果您的文件位于不同的目录下,您需要在路径前添加 /;例如 initialization/Lifepaths.txt。如果文件的路径是 same 的你的类(但在资源作为主目录下),你可以只放文件名而不带任何 /。例如,如果您的类具有以下路径 src/main/java/paths/Lifepaths.java,则您的文件必须具有此路径 src/main/resources/paths/Lifepaths.txt
P
Puce

规则如下:

检查要在 JAR 中加载的文件的位置(并确保它实际添加到 JAR) 使用绝对路径:路径从 JAR 的根目录开始 使用相对路径:路径从包开始您正在调用的类的目录 getResource/getResoucreAsStream

并尝试:

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

代替

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

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


我已经完成了所有这三件事。请重新阅读我的问题。
从您的问题中,不清楚“相关目录结构”是什么以及您是否实际检查了文件是否以及该文件在 JAR 中的位置(步骤 1)
您关于相对路径的评论最终解决了我的问题;谢谢!
有趣的。事实证明我必须做“/config.properties”(带斜杠)才能得到它......
g
greedybuddha

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

我见过的最好的解释是来自 InfoWorld 的这篇文章。我将在这里进行总结,但是如果您想了解更多信息,则应查看该文章。

方法

类加载器.getResourceAsStream()。

格式:“/”-分隔名称;没有前导“/”(所有名称都是绝对的)。

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

类.getResourceAsStream()

格式:“/”-分隔名称;前导“/”表示绝对名称;所有其他名称都相对于类的包

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

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


你的第二个例子坏了
第二个示例没有损坏,正如我在答案中所说,它取决于资源所在的位置。
另外:确保您的 IDE 通过刷新源文件夹可以看到文件('some/pkg/resource.properties')。
JavaWorld 的链接已断开。
如果它可以帮助任何人,我的根 /target/classes。因此,如果我使用方法一加载文件“test.json”,则该文件需要位于已编译代码或相应 jar 文件中的 /target/classes/test.json 中。
P
Paul Smith

不要使用绝对路径,使它们相对于项目中的“资源”目录。快速而肮脏的代码,显示目录“资源”中的 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);
}

S
Shijing Lv

大致说来:

getClass().getResource("/") ~= Thread.currentThread().getContextClassLoader().getResource(".")

假设你的项目结构如下:

├── 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


这个解释为我澄清了很多问题。谢谢!
从来没有意识到 Class 的 getResource 方法与 ClassLoader 的 getResources 方法的根不同。这个答案应该在页面的顶部。
救了我一命!!!!视觉答案是最好的部分
C
Community

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

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

我曾经有一个类似的问题:Reading txt file from jar fails but reading image works


这正是 getResourceAsStream() 所做的
p
plosco

我发现自己遇到了类似的问题。由于我使用的是 maven,我需要更新我的 pom.xml 以包含以下内容:

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

请注意其中的资源标签以指定该文件夹的位置。如果您有嵌套项目(就像我一样),那么您可能希望从其他区域获取资源,而不仅仅是在您正在工作的模块中。如果您使用类似的配置数据,这有助于减少在每个 repo 中保留相同的文件


B
Binita Bharati

您正在使用的 ClassLoader 似乎存在问题。使用 contextClassLoader 加载类。这与它是否在静态/非静态方法中无关

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


g
gavenkoa

https://jrebel.com/wp-content/uploads/2013/08/classloader-hierarchy.jpg

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

J
John Manko

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

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

我不需要将此文件显式添加到路径中(显然,将其添加到 /src 即可)


错误的java语法
L
Leo Ufimtsev

不知道是否有帮助,但在我的情况下,我的资源位于 /src/ 文件夹中并且出现此错误。然后我将图片移动到 bin 文件夹并解决了问题。


B
Boris Brodski

确保您的资源目录(例如“src”)在您的类路径中(确保它是 Eclipse 中构建路径中的源目录)。

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

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

clazz.getResourceAsStream("/initialization/Lifepaths.txt");

原因:clazz.getResourcesAsStream(foo)clazz 的类路径中 查找 foo,相对于 clazz 所在的目录。前导的“/”使它从 clazz 的类路径中任何目录的根目录加载。

除非您在某种容器中,例如 Tomcat,或者直接使用 ClassLoaders,否则您可以将 eclipse/命令行类路径视为唯一的类加载器类路径。


F
Feku279

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

<packaging>jar</packaging>

C
ConAim

您真正需要的是文件的完整绝对类路径。因此,与其猜测,不如尝试找出根目录,然后将文件移动到基于 <.war> 文件结构的更好位置...

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

A
Akash Yellappa

对我有用的是我将文件放在

src/main/java/myfile.log

InputStream is = getClass().getClassLoader().getResourceAsStream("myfile.log");
        
        if (is == null) {
            throw new FileNotFoundException("Log file not provided");
        }

源文件夹称为 src/main/JAVA,显然您的非代码文件不应位于此处。
t
tcsnzh

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

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

src java 资源 conf config.json log4j2.xml openapi.yaml

爪哇

资源 conf config.json log4j2.xml openapi.yaml

配置文件.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");

否则,如果您需要在 root 中阅读其他内容,则只需:

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

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


正如目前所写的那样,您的答案尚不清楚。请edit添加其他详细信息,以帮助其他人了解这如何解决所提出的问题。您可以找到有关如何写出好答案的更多信息in the help center
S
Shahid Hussain Abbasi

请删除

或包含您尝试阅读的文件


S
Shahid Hussain Abbasi

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


q
qwebek

我的呼叫者班级在 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/

爪哇 11


m
mbmast

虽然不打算作为答案,但我将此建议作为答案提出,以便我可以获取屏幕截图。

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

Process Monitor 是 Microsoft 提供的一套实用程序中的一个实用程序(您甚至无需登录即可下载)。它们都可以在 sysinternals.com 上找到,这是 Microsoft 收购并保留的原始主机。

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

https://i.stack.imgur.com/34IQC.png

在这里,我正在寻找(而不是找到)模式文件 (XSD),以便代码可以使用它来验证某些用户提供的 XML。


j
j5shi

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

假设你在'resources/'中有一个名为'book.json'的文件,那么要使用其中的资源,你可以这样做

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

https://i.stack.imgur.com/4bXsr.png


A
Amit

Lifepaths.class.getClass()。 getResourceAsStream("Lifepaths.txt"));


A
Anshul Goyal

@Emracool ...我建议您选择一个替代方案。由于您似乎正在尝试加载 *.txt 文件。最好使用 FileInputStream() 而不是这个烦人的 getClass().getClassLoader().getResourceAsStream()getClass().getResourceAsStream()。至少您的代码将正确执行。


什么 ??? -1 对于这样的工作答案。无论。但上述建议的解决方案肯定会奏效。