问题
当应用程序打包为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 -> Build Artifacts...
在IntelliJ中构建jar。 resources
目录在IntelliJ中标记为资源根,这是我的pom.xml。
生成的jar文件确实包含我认为它应具有的所有资源。也就是说,它具有以下目录树:
application.jar
├── images
| ├── img1.jpg
| └── img2.png
├── fonts
| └── font.ttf
├── META-INF
├── Main.class
└── stylesheet.css
我在发帖之前咨询过的资源有Apache Maven Archiver,IntelliJ Working With Artifacts,StackOverflow NullPointerException When...,StackOverflow JavaFX and maven...以及几个类似的问题。其中许多问题解决了NullPointerException: Location is required
问题,这似乎是在从xml加载JavaFX布局时发生的,我没有这样做,也不是我得到的错误。
请注意,我几乎没有Maven的经验,只有文件流,类加载器等基本概念以及JavaFX的一些经验。
答案 0 :(得分:0)
这个问题的两个答案。
加载字体只是我的错字,我在路径名中使用了错误的大小写。有趣的是,这个拼写错误只是在运行jar文件时没有加载字体,当从IntelliJ运行应用程序时没关系。因此,您可以在jar中加载字体,如下所示:
Font.loadFont(Memory.class.getResourceAsStream("fonts/Font.ttf"), 20);
如果没有解决方法,从jar中加载文件而不知道其确切名称似乎是不可能的。您必须获得对jar文件本身的引用并枚举其条目。 this question的答案中描述了这一点,Adi在评论中与{{3}}相关联。
因此,我能够像这样加载我的图像:
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链接到的问题的答案中略有改变。