我使用 JPackageScriptFX 将 JavaFX 项目打包为安装程序,并且创建了一个名为“resources”的文件夹,该文件夹位于我的源包的 basedir 中。我已通过传递
--app-content resources
标志将此文件夹包含在我的包中:
$JAVA_HOME/bin/jpackage \
--type $INSTALLER_TYPE \
--dest target/installer \
--input target/libs \
--app-content resources \
--name "$NAME" \
--vendor "$VENDOR" \
--main-class ${MAIN_CLASS}.AppLauncher \
--main-jar ${MAIN_JAR} \
--java-options -Xmx2048m \
--runtime-image target/java-runtime \
--icon ${ICON} \
--app-version ${APP_VERSION} \
这是我的源代码文件夹目录结构:
Project
|
|-resources
| |-icon.png
|
|-src
|
|-pom.xml
.
.
.
软件包创建并安装成功后,安装目录结构为:
Project
|
|-bin
| |-project
|
|-lib
| |-app
| | |-project.jar
| | |-project.cfg
| |
| |-resources
| | |-icon.png
| |
| |-runtime
|
|-share
当我运行该项目时,我得到了
FileNotFoundException
on 方法:
DBIO.loadResource
public class DBIO {
private DBIO(){/*NOTHING*/}
private static InputStream loadResource(String path){
try{
return new FileInputStream("resources/"+path);
}catch(IOException ex){
ex.printStackTrace();
}
return null;
}
public static final Image LOGO = new Image(loadResource("icon.png"));
}
java.io.FileNotFoundException: resources/icon.png (No such file or directory)
at java.base/java.io.FileInputStream.open0(Native Method)
at java.base/java.io.FileInputStream.open(Unknown Source)
at java.base/java.io.FileInputStream.<init>(Unknown Source)
at java.base/java.io.FileInputStream.<init>(Unknown Source)
at project.app.DBIO.loadResource(DBIO.java:24)
at project.app.DBIO.<clinit>(DBIO.java:30)
at project.app.EULAController.<clinit>(EULAController.java:29)
at java.base/jdk.internal.misc.Unsafe.ensureClassInitialized0(Native Method)
at java.base/jdk.internal.misc.Unsafe.ensureClassInitialized(Unknown Source)
at java.base/jdk.internal.reflect.MethodHandleAccessorFactory.ensureClassInitialized(Unknown Source)
at java.base/jdk.internal.reflect.MethodHandleAccessorFactory.newConstructorAccessor(Unknown Source)
at java.base/jdk.internal.reflect.ReflectionFactory.newConstructorAccessor(Unknown Source)
at java.base/java.lang.reflect.Constructor.acquireConstructorAccessor(Unknown Source)
at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Unknown Source)
at java.base/java.lang.reflect.Constructor.newInstance(Unknown Source)
at javafx.fxml.FXMLLoader$ValueElement.processAttribute(FXMLLoader.java:941)
at javafx.fxml.FXMLLoader$InstanceDeclarationElement.processAttribute(FXMLLoader.java:983)
at javafx.fxml.FXMLLoader$Element.processStartElement(FXMLLoader.java:230)
at javafx.fxml.FXMLLoader$ValueElement.processStartElement(FXMLLoader.java:757)
at javafx.fxml.FXMLLoader.processStartElement(FXMLLoader.java:2853)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2649)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2563)
at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2531)
at project.app.App.start(App.java:16)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$9(LauncherImpl.java:839)
at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$12(PlatformImpl.java:483)
at com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:456)
at java.base/java.security.AccessController.doPrivileged(Unknown Source)
at com.sun.javafx.application.PlatformImpl.lambda$runLater$11(PlatformImpl.java:455)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
at com.sun.glass.ui.gtk.GtkApplication._runLoop(Native Method)
at com.sun.glass.ui.gtk.GtkApplication.lambda$runLoop$10(GtkApplication.java:263)
at java.base/java.lang.Thread.run(Unknown Source)
Exception in Application start method
Exception in thread "main" java.lang.RuntimeException: Exception in Application start method
at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:893)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$2(LauncherImpl.java:196)
at java.base/java.lang.Thread.run(Unknown Source)
Caused by: java.lang.ExceptionInInitializerError
at project.app.EULAController.<clinit>(EULAController.java:29)
at java.base/jdk.internal.misc.Unsafe.ensureClassInitialized0(Native Method)
at java.base/jdk.internal.misc.Unsafe.ensureClassInitialized(Unknown Source)
at java.base/jdk.internal.reflect.MethodHandleAccessorFactory.ensureClassInitialized(Unknown Source)
at java.base/jdk.internal.reflect.MethodHandleAccessorFactory.newConstructorAccessor(Unknown Source)
at java.base/jdk.internal.reflect.ReflectionFactory.newConstructorAccessor(Unknown Source)
at java.base/java.lang.reflect.Constructor.acquireConstructorAccessor(Unknown Source)
at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Unknown Source)
at java.base/java.lang.reflect.Constructor.newInstance(Unknown Source)
at javafx.fxml.FXMLLoader$ValueElement.processAttribute(FXMLLoader.java:941)
at javafx.fxml.FXMLLoader$InstanceDeclarationElement.processAttribute(FXMLLoader.java:983)
at javafx.fxml.FXMLLoader$Element.processStartElement(FXMLLoader.java:230)
at javafx.fxml.FXMLLoader$ValueElement.processStartElement(FXMLLoader.java:757)
at javafx.fxml.FXMLLoader.processStartElement(FXMLLoader.java:2853)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2649)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2563)
at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2531)
at project.app.App.start(App.java:16)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$9(LauncherImpl.java:839)
at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$12(PlatformImpl.java:483)
at com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:456)
at java.base/java.security.AccessController.doPrivileged(Unknown Source)
at com.sun.javafx.application.PlatformImpl.lambda$runLater$11(PlatformImpl.java:455)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
at com.sun.glass.ui.gtk.GtkApplication._runLoop(Native Method)
at com.sun.glass.ui.gtk.GtkApplication.lambda$runLoop$10(GtkApplication.java:263)
... 1 more
Caused by: java.lang.NullPointerException: Cannot invoke "java.io.File.toPath()" because "file" is null
at project.app.DBIO.writeToFile(DBIO.java:73)
at project.app.Util.log(Util.java:43)
at project.app.DBIO.loadResource(DBIO.java:26)
at project.app.DBIO.<clinit>(DBIO.java:30)
... 27 more
编辑: 如果我将资源文件夹复制到
Project/bin
中,项目就会开始工作。这意味着程序正在 bin 内寻找资源文件夹。我应该如何将其指向正确的位置(在/lib
内)?
前言:
通过覆盖 jpackage 使用的资源(例如属性和脚本的背景图像和模板文件),可以对生成的包进行高级自定义。
--resource-dir
选项用于为工具提供覆盖。
如果 jpackage 在打包应用程序时使用的默认资源不能满足您的需求,请创建一个目录并向其中添加您的自定义文件。如果覆盖文件,则自定义文件必须包含默认文件包含的所有属性。使用 --resource-dir 选项将目录路径传递给 jpackage。路径可以是绝对路径或相对于当前目录的路径。
(https://docs.oracle.com/en/java/javase/21/jpackage/override-jpackage-resources.html)
关于“--resource-dir”的解释:
覆盖 jpackage 资源的路径。 它可以是绝对路径或相对于当前目录的路径。
可以通过向此目录添加替换资源来覆盖 jpackage 的图标、模板文件和其他资源。
(https://docs.oracle.com/en/java/javase/21/docs/specs/man/jpackage.html)
附加信息:
如果您使用外部资源目录,那么您将面临这样的问题:您的 IDE 在开发过程中也必须使用该目录。您可能希望使用定制的“开关”在开发期间和项目打包时指向正确的资源目录。
或者您可以重写代码以在 JAR 中查找资源,如果那里不可用,请查看相对于您的 JAR 的指定目录。