【问题标题】:How to load resources when JavaFX app built as IntelliJ Maven project is packaged as JAR?构建为 IntelliJ Maven 项目的 JavaFX 应用程序打包为 JAR 时如何加载资源?
【发布时间】:2018-11-23 17:42:05
【问题描述】:

问题

当应用程序被打包为 Jar 文件时,我应该怎么做才能在我的 JavaFX 应用程序中加载资源?

背景

该应用程序是 IntelliJ IDEA 中的一个 Maven 项目。直接从 IntelliJ 运行应用程序适用于下面描述的所有情况,但我从项目构建的 jar 文件大多无法加载资源(图像和字体)。

项目目录树

application
├── .idea
├── out
├── src
|   ├── main
|   |   ├── java
|   |   |   ├── META-INF
|   |   |   └── Main.java
|   |   └── resources
|   |       ├── images
|   |       |   ├── img1.jpg
|   |       |   └── img2.png
|   |       ├── fonts
|   |       |   └── font.ttf
|   |       └── stylesheet.css
|   └── test
├── target
├── application.iml
└── pom.xml

加载图片的工作方式如下:

Image img1 = new Image(this.getClass().getResource("images/img1.jpg").toString());
Image img2 = new Image(this.getClass().getResource("images/img2.png"));

但由于我实际上想加载图像文件夹中的所有图像而不对它们的名称进行硬编码,所以我一直在这样做:

File dir = new File(this.getClass().getResource("images").getFile());
List<File> imageFiles = new ArrayList<>(Arrays.asList(dir.listFiles()));
List<Image> images = new ArrayList<>();
for (int i = 0; i < imageFiles.size(); i++) {
   images.add(new Image("images/" + imageFiles.get(i).getName()));
}

当应用程序被打包成 jar 文件并在第 2 行导致 NullPointerException 时,这不起作用。事实证明 dir.listFiles() 返回 null 而 dir.exists() 返回 false。

加载字体我尝试了两种方式。像这样在stylesheet.css:

@font-face {
   font-family: 'Font';
   src: url('fonts/font.ttf');
}

或者像这样作为Main的start方法的第一行:

Font.loadFont(getClass().getResourceAsStream("fonts/font.ttf"), 20);

在任何一种情况下,我都通过样式表应用字体。从 IntelliJ 中运行应用程序时,这两种情况都有效,但在运行 jar 时均无效。第一种方法打印错误信息

Jun 14, 2018 4:21:47 AM com.sun.javafx.css.StyleManager loadStylesheetUnPrivileged
INFO: Could not load @font-face font [jar:file:/D:/path/in/file/system/java/application/out/artifacts/application_jar/application.jar!/fonts/font.ttf]

第二种方法静默失败。

我做了什么和其他信息

我正在通过Build -&gt; Build Artifacts... 在 IntelliJ 中构建 jar。 resources 目录在 IntelliJ 中标记为 Resources Root,这是我的 pom.xml

生成的 jar 文件确实包含我认为它应该具有的所有资源。也就是说,它有以下目录树:

application.jar
├── images
|   ├── img1.jpg
|   └── img2.png
├── fonts
|   └── font.ttf
├── META-INF
├── Main.class
└── stylesheet.css

在发表这篇文章之前我咨询过的资源是Apache Maven ArchiverIntelliJ Working With ArtifactsStackOverflow NullPointerException When...StackOverflow JavaFX and maven... 和几个类似的问题。他们中的许多人解决了NullPointerException: Location is required 问题,这似乎是在从 xml 加载 JavaFX 布局时发生的,我没有这样做,也不是我得到的错误。

请注意,我以前几乎没有使用过 Maven,只有文件流、类加载器等的基本概念以及一些 JavaFX 经验。

【问题讨论】:

  • 你试过this.getClass().getResource("yourpath").toExternalForm()吗?
  • @Adi 接受的答案适用于加载图像,谢谢!我觉得很奇怪,似乎没有更好的方法可以在不知道资源名称的情况下在 jar 中加载资源。

标签: java maven intellij-idea resources embedded-resource


【解决方案1】:

这个问题有两个答案。

加载字体只是我的一个错字,我在路径名中使用了错误的大小写。有趣的是,这个错字只会在运行 jar 文件时导致字体不加载,而从 IntelliJ 运行应用程序时则无关紧要。因此,您可以像这样从 jar 中加载字体:

Font.loadFont(Memory.class.getResourceAsStream("fonts/Font.ttf"), 20);

从 jar 中加载文件,而不知道它们的确切名称,如果没有解决方法似乎是不可能的。您必须获取对 jar 文件本身的引用并枚举其条目。这在Adi 在他的评论中链接到的this question 的答案中有所描述。

因此,我能够像这样加载我的图像:

List<String> imagePaths = getPaths("images");
List<Image> imagePool = new ArrayList<>();
for (int i = 0; i < imagePaths.size(); i++) {
   imagePool.add(new Image(imagePaths.get(i)));
}

getPaths 在哪里

/** Returns the paths to all files in a folder. Useful for loading many files without knowing their names.
 * Importantly, this function will work whether or not the application is run from a jar or not.
 * However it might not work if the jar is not run locally.
 *
 * @param folderPath The relative path to the folder with no ending slash.
 * @return A List of path names to all files in that folder.
 * @throws IOException
 */
public static List<String> getPaths(String folderPath) throws IOException {
    final File jarFile = new File(Memory.class.getProtectionDomain().getCodeSource().getLocation().getPath());
    List<String> filePaths = new ArrayList<>();

    if(jarFile.isFile()) {  // Run with JAR file
        final JarFile jar = new JarFile(jarFile);
        final Enumeration<JarEntry> entries = jar.entries(); //gives ALL entries in jar
        while(entries.hasMoreElements()) {
            final String name = entries.nextElement().getName();
            if (name.startsWith(folderPath + "/")) { //filter according to the folderPath
                filePaths.add(name);
            }
        }
        jar.close();
    } else { // Run with IDE
        final URL url = Memory.class.getResource("/" + folderPath);
        if (url != null) {
            try {
                final File apps = new File(url.toURI());
                for (File app : apps.listFiles()) {
                    filePaths.add(folderPath + "/" + app.getName());
                }
            } catch (URISyntaxException ex) {
                // never happens
            }
        }
    }
    return filePaths;
}

这与 Adi 所链接的问题的答案略有不同。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-06-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-27
    • 1970-01-01
    • 2013-04-21
    • 1970-01-01
    相关资源
    最近更新 更多