我正在从我的 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 中。
我该如何解决这个问题?
Lifepaths.class
调用它。话虽如此,为什么 getClassLoader()
允许它工作? (另外,请随时发布答案!)
Lifepaths.getClass()
吗? Object 中没有定义这样的静态方法...
getResource(String)
让它工作。顺便说一句 - 我一直无法让其中任何一个在 static
上下文中工作。问题基本上是获得的类加载器是用于 J2SE 类的。您需要访问适用于应用程序本身的 context 类加载器。
Lifepaths.class.getClass().getResourceAsStream(...)
使用系统类加载器加载资源,它显然会失败,因为它看不到您的 JAR
Lifepaths.class.getResourceAsStream(...)
使用加载 Lifepaths 类的同一个类加载器加载资源,并且它应该可以访问您的 JAR 中的资源
规则如下:
检查要在 JAR 中加载的文件的位置(并确保它实际添加到 JAR) 使用绝对路径:路径从 JAR 的根目录开始 使用相对路径:路径从包开始您正在调用的类的目录 getResource/getResoucreAsStream
并尝试:
Lifepaths.class.getResourceAsStream("/initialization/Lifepaths.txt")
代替
Lifepaths.class.getClass().getResourceAsStream("/initialization/Lifepaths.txt")
(不确定是否有区别,但前者将使用正确的 ClassLoader/JAR,而我不确定后者)
因此,有几种方法可以从 jar 中获取资源,每种方法的语法略有不同,需要以不同的方式指定路径。
我见过的最好的解释是来自 InfoWorld 的这篇文章。我将在这里进行总结,但是如果您想了解更多信息,则应查看该文章。
方法
类加载器.getResourceAsStream()。
格式:“/”-分隔名称;没有前导“/”(所有名称都是绝对的)。
示例:this.getClass().getClassLoader().getResourceAsStream("some/pkg/resource.properties");
类.getResourceAsStream()
格式:“/”-分隔名称;前导“/”表示绝对名称;所有其他名称都相对于类的包
示例:this.getClass().getResourceAsStream("/some/pkg/resource.properties");
2020 年 9 月更新:更改了文章链接。原始文章来自 Javaworld,现在托管在 InfoWorld 上(并且有更多广告)
不要使用绝对路径,使它们相对于项目中的“资源”目录。快速而肮脏的代码,显示目录“资源”中的 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);
}
大致说来:
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
您可能想尝试这个来获取流,即首先获取 url,然后将其作为流打开。
URL url = getClass().getResource("/initialization/Lifepaths.txt");
InputStream strm = url.openStream();
我曾经有一个类似的问题:Reading txt file from jar fails but reading image works
我发现自己遇到了类似的问题。由于我使用的是 maven,我需要更新我的 pom.xml 以包含以下内容:
...
</dependencies>
<build>
<resources>
<resource>
<directory>/src/main/resources</directory>
</resource>
<resource>
<directory>../src/main/resources</directory>
</resource>
</resources>
<pluginManagement>
...
请注意其中的资源标签以指定该文件夹的位置。如果您有嵌套项目(就像我一样),那么您可能希望从其他区域获取资源,而不仅仅是在您正在工作的模块中。如果您使用类似的配置数据,这有助于减少在每个 repo 中保留相同的文件
您正在使用的 ClassLoader 似乎存在问题。使用 contextClassLoader 加载类。这与它是否在静态/非静态方法中无关
Thread.currentThread().getContextClassLoader().getResourceAsStream
......
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);
对我有用的是在 My Project/Java Resources/src
下添加文件,然后使用
this.getClass().getClassLoader().getResourceAsStream("myfile.txt");
我不需要将此文件显式添加到路径中(显然,将其添加到 /src
即可)
不知道是否有帮助,但在我的情况下,我的资源位于 /src/ 文件夹中并且出现此错误。然后我将图片移动到 bin 文件夹并解决了问题。
确保您的资源目录(例如“src”)在您的类路径中(确保它是 Eclipse 中构建路径中的源目录)。
确保从主类加载器加载 clazz。
然后,要加载 src/initialization/Lifepaths.txt,请使用
clazz.getResourceAsStream("/initialization/Lifepaths.txt");
原因:clazz.getResourcesAsStream(foo)
从 clazz 的类路径中 查找 foo,相对于 clazz 所在的目录。前导的“/”使它从 clazz 的类路径中任何目录的根目录加载。
除非您在某种容器中,例如 Tomcat,或者直接使用 ClassLoaders,否则您可以将 eclipse/命令行类路径视为唯一的类加载器类路径。
如果您使用 Maven,请确保您的包装是“jar”而不是“pom”。
<packaging>jar</packaging>
您真正需要的是文件的完整绝对类路径。因此,与其猜测,不如尝试找出根目录,然后将文件移动到基于 <.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());
对我有用的是我将文件放在
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
,显然您的非代码文件不应位于此处。
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)} 知道为什么~
请删除
或包含您尝试阅读的文件
在 pom.xml 中管理或删除 ../src/main/resources
我的呼叫者班级在 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
虽然不打算作为答案,但我将此建议作为答案提出,以便我可以获取屏幕截图。
对于那些在 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。
如果您使用的是 IDEA,请确保将您的资源文件夹标记为“资源”,以便 IDE 可以正确识别路径。
假设你在'resources/'中有一个名为'book.json'的文件,那么要使用其中的资源,你可以这样做
InputStream input = Main.class.getResourceAsStream("/book.json");
https://i.stack.imgur.com/4bXsr.png
Lifepaths.class.getClass()。 getResourceAsStream("Lifepaths.txt"));
@Emracool ...我建议您选择一个替代方案。由于您似乎正在尝试加载 *.txt 文件。最好使用 FileInputStream()
而不是这个烦人的 getClass().getClassLoader().getResourceAsStream()
或 getClass().getResourceAsStream()
。至少您的代码将正确执行。
不定期副业成功案例分享
/
;例如initialization/Lifepaths.txt
。如果文件的路径是 same 的你的类(但在资源作为主目录下),你可以只放文件名而不带任何/
。例如,如果您的类具有以下路径src/main/java/paths/Lifepaths.java
,则您的文件必须具有此路径src/main/resources/paths/Lifepaths.txt
。