getResourceAsStream返回null


182

我正在从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"

这些都不起作用。 了许多 问题至今的话题,但他们都不是有帮助的-通常情况下,他们只是说,使用根路径,这我已经在做负载文件。那,或者只是从当前目录加载文件(只是load filename),我也尝试过。该文件将使用适当的名称编译到JAR的适当位置。

我该如何解决?


3
你检查它真的在jar文件?你检查过文件外壳了吗?
乔恩·斯基特

@JonSkeet实际上确实在适当位置将其编译到JAR文件中,并且大小写正确。

1
@greedybuddha虽然无法从静态上下文中调用它,但可以使用调用它Lifepaths.class。话虽这么说,为什么要getClassLoader()让它工作呢?(也可以随时发布答案!)

你能证明 Lifepaths.getClass()吗?在Object ...中没有定义这样的静态方法
Puce,

1
看看这个答案,看看是否可以使用来工作getResource(String)。顺便说一句-我一直很难让任何一个在static上下文中工作。问题基本上是获得的类装入器是针对J2SE类的类装入器。您需要访问用于应用程序本身的上下文类加载器。
安德鲁·汤普森

Answers:


158

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

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


128
只是为了补充,在调用getResourceAsStream(name)时,名称必须以“ /”开头。我不确定这是否必要,但如果没有它,我会遇到问题。
戴维(David)

3
从今天/昨天早上8点开始,我一直在搞这个。救了我。我也确实需要一个斜杠来使其正常工作。
凯尔

4
还请记住,所需的源可以在包层次结构之外。在这种情况下,您必须在路径中使用“ ../”来获得一个级别,然后下降到另一个路径分支以获取您的资源。
Zon

3
@David-我认为(以'/'开头)是必要的,否则它会相对于Lifepaths.class包进行搜索
Mz A

5
仅添加一些信息,/如果文件位于其他目录下,则需要在路径前添加;例如initialization/Lifepaths.txt。如果文件的路径 yout类相同(但在资源下为主目录),则可以仅输入文件名,而不添加任何文件名/。例如,如果您的班级具有以下路径src/main/java/paths/Lifepaths.java,则文件必须具有该路径src/main/resources/paths/Lifepaths.txt
Dwhitz

59

规则如下:

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

并尝试:

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

代替

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

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


2
我已经完成了所有这三件事。请重读我的问题。

从您的问题尚不清楚“相关目录结构”是什么,以及您是否实际上已经检查了文件是否在JAR中以及在JAR中的位置(步骤1)
Puce,

1
您关于相对路径的评论终于解决了我的问题。谢谢!
Paul Bormans,2016年

有趣。事实证明,我必须做“ /config.properties”(带有斜线)才能到达……
Erk

45

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

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

方法

1) ClassLoader.getResourceAsStream()

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

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

2) Class.getResourceAsStream()

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

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


您的第二个示例已损坏
Janus Troelsen

1
第二个例子没有坏,正如我在回答中所说的,它取决于资源所在的位置。
greedybuddha '16

另外:通过刷新源文件夹,确保您的IDE可以看到文件(“ some / pkg / resource.properties”)。
Eric Duminil

15

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


8

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

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


5

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

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

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


3

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

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

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


错误的Java语法
Ilya Kharlamov

2

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


2

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

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

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

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

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

除非您使用的是Tomcat之类的容器,或者直接使用ClassLoaders做某事,否则您可以将eclipse /命令行classpath视为唯一的classloader类路径。


2

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

Lifepaths.class.getClass()的类加载器是bootstrap classloader,因此getResourceAsStream无论用户提供,都将仅搜索$ JAVA_HOME classpath。显然,Lifepaths.txt不存在。

Lifepaths.class的classloader是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); 

2

大致说来:

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

假设您的项目结构如下:

├── src
   ├── main
   └── test
   └── test
       ├── java
          └── com
              └── github
                  └── xyz
                      └── proj
                          ├── MainTest.java
                          └── TestBase.java
       └── resources
           └── abcd.txt
└── target
    └── test-classes
        ├── com
        └── abcd.txt
// in MainClass.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

2

如果您使用的是Maven,请确保您的包装是“罐子”而不是“绒球”。

<packaging>jar</packaging>

0

您真正需要的是文件的完整绝对classPath。因此,不要猜测,而是尝试找出根目录,然后将文件移动到一个更好的位置,以<.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());

-1

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

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,显然您的非代码文件不应位于此处。
艾琳

-12

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


什么 ???-1为此类工作答案。无论。但是以上建议的解决方案肯定可以工作。
Jain 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.