如何告诉 Java JAR 文件中的正确文件路径?

How to tell Java the correct file path in a JAR-file?

我已将所有 JavaFX 代码放入可执行 JAR 文件中。一切都在里面。

当我用 Eclipse IDE 编译它时,它可以工作,但是当我尝试执行 JAR 文件时,出现错误。

Caused by: javafx.fxml.LoadException: 
file:/home/asus/program/JUSBPlotter/JUSBPlotter-0.0.1-SNAPSHOT-jar-with-dependencies.jar!/se/danielmartensson/fxml/front.fxml

当我有这个 Java 代码时

 Parent root = FXMLLoader.load(getClass().getResource("/se/danielmartensson/fxml/front.fxml"));

FXML 文件没有问题。

fx:controller="se.danielmartensson.controller.Front"

我尝试在 JavaFX 中显示图片时也出现错误。

Caused by: java.io.FileNotFoundException: file:/home/asus/program/JUSBPlotter/JUSBPlotter-0.0.1-SNAPSHOT-jar-with-dependencies.jar!/se/danielmartensson/pictures/computer.png (No such file or directory)

当我有这个 Java 代码时:

FileInputStream picture1 = new FileInputStream(getClass().getResource("/se/danielmartensson/pictures/computer.png").getFile());

问题: 我如何告诉 Java JAR 文件中正确的文件路径?

要记住的一件事是资源与文件不同,尽管它们看起来非常相似——尤其是在打包应用程序之前。打包应用程序后,这些资源文件将成为 JAR 文件中的条目,虽然可以将它们视为文件,但无法像访问文件一样访问这些文件。特别是,java.io.File API 不理解如何阅读这些条目。但是,您可以通过 Class#getResource(String) or the InputStream returned by Class#getResourceAsStream(String). Note the latter basically just finds the URL and, if it exists, calls URL#openStream(). The reason you can access JAR entries (i.e. resources) with the URL is due to the JarURLConnection 和相关 class 返回的 URL 访问这些条目;我不会详细介绍 Java 如何处理 URL,因为这超出了这个答案的范围。

现在,由于您正尝试在 JavaFX 中显示图像,我假设您使用的是 Image class。基本上可以通过三种方式告诉 Image 图片资源在哪里。

  1. 在参数 URL 没有方案的地方使用 Image#<init>(String)。在这种情况下,class 会将 URL 视为相对于 class 路径根的路径,并相应地尝试访问图像。基本上,该参数与您给 Class#getResource(String) 的参数相同,除了路径必须是绝对路径(即不相对于任何 class)。此行为由 Image class:

    记录

    All URLs supported by URL can be passed to the constructor. If the passed string is not a valid URL, but a path instead, the Image is searched on the classpath in that case.

    注意:如果您使用的是模块,则 resource encapsulation comes into play when using this option. See GitHub Issue #441.

  2. 使用与上面相同的构造函数,但传递一个有效的 URL(即它有一个方案)。使用资源时,您可以使用 URL#toExternalForm().

    Class#getResource(Sring) 返回的 URL 简单地转换为 String
    Image image = new Image(getClass().getResource(...).toExternalForm());
    

    注意:您也可以使用URL#toString()其中returns相同的值。

  3. 使用Image#<init>(InputStream)。这里可以使用Class#getResourceAsStream(String)。不要忘记在完成后关闭 InputStream

    try (InputStream is = getClass().getResourceAsStream(...)) {
        Image image = new Image(is);
    }
    

所有这些都假定在您构建 JAR 文件时资源已正确打包。如果不是这种情况,请首先遵循 José Pereda 在问题评论中给出的建议。


请注意,可以使用 java.util.zip.* API、java.util.jar.* API 访问 ZIP/JAR 文件的条目,或者 Java NIO 的 ZipFileSystemProvider。其中每一个,尤其是最后一个选项,都允许您访问类似于实际文件的 ZIP/JAR 文件的条目,因为它们是专门为此设计的。但是,在应用程序中使用资源时,您应该坚持使用 Class#getResource(String) 和相关方法。