ClassLoader.getResource returns 奇怪的路径(也许)?

ClassLoader.getResource returns odd path (maybe)?

从resources文件夹中加载文本文件等资源时,最常见的做法是使用ClassLoader获取路径:

String path = getClass().getClassLoader().getResource("file.txt").getPath();

然后您可以使用 java 拥有的众多阅读器中的任何一个阅读该文件的内容。但出于某种原因,Paths.get(path) 对路径不满意:

byte[] content = Files.readAllBytes(Paths.get(path)) 
    -> throws java.nio.file.InvalidPathException when executed

ClassLoader.getResource(...).getPath() 正在返回:

/D:/Projects/myapp/build/resources/main/file.txt

Paths.get() 不喜欢。显然 /D 之后的 ':' 是一个 'Illegal char'。 (注意路径好像是对的,文件其实是有的)

是哪个问题导致的? ClassLoader.getResource() 返回的是无效路径还是 Paths.get() 胡作非为?


一段时间后
java 中的路径似乎有多种不同的格式。各种框架似乎并不完全同意什么是对的,什么是错的,因此它们创建和接受的路径之间存在各种差异。

在此示例中,Paths.get() 实际上并不期望路径中的前导斜杠:

/D:/Projects/myapp/build/resources/main/vertex.vs.glsl <- EVIL
D:/Projects/myapp/build/resources/main/vertex.vs.glsl <- OK

我想现在的问题是:如何清理 ClassLoader.getResource() 返回的文件路径以便与 Paths.get() 一起正确使用?它们的两种文件路径格式之间还有其他区别吗?

  1. “最常见的方法”不一定是最好的:)
  2. 注意你指的是哪条路径:ClassLoader.getResource() returns a URL, which can have a path 组件。但是,这不一定是有效的文件路径。
    请注意,还有一个方法 Paths.get(URI) which takes a URI 作为参数
  3. /D:/Projects/myapp/build/resources/main/file.txt 中的第一个斜杠只是表示这是一个绝对路径:参见 Class.getResource
  4. 我建议,当您想要读取文件时,您只需使用 ClassLoader.html#getResourceAsStream

Update 回答评论:“那么为什么 Paths.get() 不接受绝对路径?”

Paths.get() 是否 接受绝对路径。
但是您必须传递一个有效的(文件)路径 - 在您的情况下,您直接传递 URL-路径(这不是有效的文件路径)。

  • 当你调用:getClass().getClassLoader().getResource("file.txt")它returns一个URL:file:/D:/Projects/myapp/build/resources/main/file.txt
    • 此 URL 包含架构 file:
    • 和有效(绝对URL)路径:/D:/Projects/myapp/build/resources/main/file.txt
  • 你尝试直接使用这个URL路径作为文件路径,这是错误的

要将 URL 路径转换为有效的文件路径,您可以使用 Paths.get(URI) 方法,如下所示:

URL fileUrl = getClass().getClassLoader().getResource("file.txt");
Path filePath = Paths.get(fileUrl.toURI());
// now you have a valid file-path: D:/Projects/myapp/build/resources/main/file.txt

请看一下getClass().getClassLoader().getResource("file.txt")的结果。这是一个URL。使用 getPath() 然后您只需检索 URL 的路径部分,忽略协议和服务器部分。将路径部分作为文件打开可能在某些情况下可行(在文件语法和 URL 路径语法匹配的简单情况下),但不要在生产代码中这样做。

为什么?当您离开 IDE 并将您的应用程序作为 JAR 或 WAR 交付时,资源将驻留在 ZIP 压缩文件中,并且不会有您可以打开的文件 "file.txt",有只有 JAR 或 WAR 文件中的条目。

正如@TmTron 指出的那样,我也建议使用 ClassLoader.getResourceAsStream()。这在所有情况下都有效。