我使用JavaFX制作了一个应用程序,当我使用IntelliJ IDE上的“构建”按钮构建项目时,它可以正常工作。 但是,当我从gradle JavaFX项目生成的jar文件中运行时,它不起作用,并且控制台上显示以下错误。
$java -jar sample-menu-all.jar
java.io.FileNotFoundException: file:/Users/myuser/MyProjectRoot/build/libs/sample-menu-all.jar!/images/main_menu/icon.svg (No such file or directory)
at java.base/java.io.FileInputStream.open0(Native Method)
at java.base/java.io.FileInputStream.open(FileInputStream.java:220)
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:158)
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:113)
at java.base/java.io.FileReader.<init>(FileReader.java:58)
at presentation.utils.ImageUtil.loadSvgImage(ImageUtil.java:23)
at presentation.controller.MenuController.loadRegistrationButtonImage(MenuController.java:25)
at presentation.controller.MenuController.initialize(MenuController.java:20)
at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2573)
at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2466)
at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3253)
at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3210)
at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3179)
at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3152)
at javafx.fxml/javafx.fxml.FXMLLoader.load(FXMLLoader.java:3144)
at presentation.navigator.VitalBitMenuNavigator.launchNewScene(VitalBitMenuNavigator.java:22)
at presentation.navigator.VitalBitMenuNavigator.launchMenuStage(VitalBitMenuNavigator.java:33)
at presentation.MainApp.start(MainApp.java:20)
at javafx.graphics/com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$9(LauncherImpl.java:919)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runAndWait$11(PlatformImpl.java:449)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$9(PlatformImpl.java:418)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:417)
at javafx.graphics/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
javafx.fxml.LoadException:
file:/Users/myuser/MyProjectRoot/build/libs/sample-menu-all.jar!/fxml/menu.fxml
at javafx.fxml/javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader.java:2625)
at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2603)
at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2466)
at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3253)
at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3210)
at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3179)
at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3152)
at javafx.fxml/javafx.fxml.FXMLLoader.load(FXMLLoader.java:3144)
at presentation.navigator.VitalBitMenuNavigator.launchNewScene(VitalBitMenuNavigator.java:22)
at presentation.navigator.VitalBitMenuNavigator.launchMenuStage(VitalBitMenuNavigator.java:33)
at presentation.MainApp.start(MainApp.java:20)
at javafx.graphics/com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$9(LauncherImpl.java:919)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runAndWait$11(PlatformImpl.java:449)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$9(PlatformImpl.java:418)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:417)
at javafx.graphics/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
Caused by: java.lang.NullPointerException
at javafx.swing/javafx.embed.swing.SwingFXUtils.toFXImage(SwingFXUtils.java:77)
at presentation.utils.ImageUtil.loadSvgImage(ImageUtil.java:30)
at presentation.controller.MenuController.loadRegistrationButtonImage(MenuController.java:25)
at presentation.controller.MenuController.initialize(MenuController.java:30)
at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2573)
... 15 more
我的目录结构是这个
ProjectRootDir
|-- build
|-- libs
sample-menu-all.jar
|-- build.gradle
|-- src
|-- main
|-- java
|-- presentation
MainApp.java
|-- controller
|-- MenuController.java
|-- navigator
|-- MenuNavigator.java
|-- utils
|-- ImageUtile.java
|-- resources
|-- fxml
|-- menu.fxml
|-- properties
|-- string.properties
gradle文件main.java和fxml类已加载在snnipet链接中。
buildscript {
dependencies {
classpath group: 'de.dynamicfiles.projects.gradle.plugins', name: 'javafx-gradle-plugin', version: '8.8.2'
classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.4'
}
repositories {
jcenter()
mavenLocal()
mavenCentral()
}
}
plugins {
id 'java'
}
version '1.0-SNAPSHOT'
apply plugin: 'java'
apply plugin: 'application'
apply plugin: 'com.github.johnrengelman.shadow'
apply plugin: 'javafx-gradle-plugin'
mainClassName = 'presentation.MainApp'
sourceCompatibility = 1.8
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
testCompile group: 'junit', name: 'junit', version: '4.12'
// Camera lib
compile 'com.github.sarxos:webcam-capture:0.3.12'
compile 'org.slf4j:slf4j-log4j12:1.7.21'
// RxJavaFX
implementation "io.reactivex.rxjava2:rxjava:2.2.2"
// Retrofit
implementation 'com.squareup.retrofit2:retrofit:2.4.0'
implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.4.0'
// Batik (for loading AVG)
compile group: 'org.apache.xmlgraphics', name: 'batik-transcoder', version: '1.10'
}
jfx {
mainClass = 'MainApp'
vendor = 'myVendor'
}
jar {
manifest {
attributes 'Main-Class': 'presentation.MainApp'
}
from {
configurations.compile.collect {
it.isDirectory() ? it : zipTree(it)
}
}
}
sourceSets {
main {
java {
srcDirs 'src/main/java'
}
resources {
srcDirs 'src/main/resources'
}
}
}
package presentation;
import javafx.application.Application;
import javafx.stage.Stage;
import presentation.navigator.MenuNavigator;
public class MainApp extends Application {
public static Stage primaryStage;
private MenuNavigator navigator;
public MainApp() {
navigator = new MenuNavigator();
}
@Override
public void start(Stage primaryStage) throws Exception {
MainApp.primaryStage = primaryStage;
navigator.launchMenuStage(primaryStage);
}
public static void main(String[] args) {
launch(args);
}
}
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import presentation.utils.ResourceBundleUtf8Control;
import presentation.utils.config.Config;
import java.io.File;
import java.net.URL;
import java.io.IOException;
import java.util.Locale;
import java.util.ResourceBundle;
public class MenuNavigator {
private void launchNewScene(Stage stage, String $fxmlName) {
try {
URL location = getClass().getResource("/fxml/" + $fxmlName);
ResourceBundle resources = ResourceBundle.getBundle("properties.string", Locale.getDefault(), new ResourceBundleUtf8Control());
Parent root = FXMLLoader.load(location, resources);
Scene scene = new Scene(root, Config.loadDimen("dimension.app_screen_size.width"), Config.loadDimen("dimension.app_screen_size.height"));
stage.setScene(scene);
stage.setTitle(Config.loadString("string.title"));
stage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
public void launchMenuStage(Stage stage) {
launchNewScene(stage, "menu.fxml");
}
}
package presentation.controller;
import javafx.event.ActionEvent;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.image.ImageView;
import presentation.MainApp;
import presentation.utils.FileUtil;
import presentation.utils.ImageUtil;
import java.net.URL;
import java.util.ResourceBundle;
public class MenuController extends BaseController implements Initializable {
public Button userRegistrationButton;
@Override
public void initialize(URL arg0, ResourceBundle arg1) {
this.loadRegistrationButtonImage();
}
private void loadRegistrationButtonImage() {
ImageView buttonImage = new ImageView();
ImageUtil.loadSvgImage(buttonImage, "/images/main_menu/icon.svg", 71, 94);
userRegistrationButton.setGraphic(buttonImage);
userRegistrationButton.setGraphicTextGap(37.5);
}
}
package presentation.utils;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.image.ImageView;
import javafx.scene.image.WritableImage;
import org.apache.batik.transcoder.TranscoderException;
import org.apache.batik.transcoder.TranscoderInput;
import org.apache.batik.transcoder.image.PNGTranscoder;
import java.awt.image.BufferedImage;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.net.URL;
public class ImageUtil {
public static void loadSvgImage(ImageView target, String resourcePath, int withd, int height) {
SvgTranscoder imageTranscoder = new SvgTranscoder();
imageTranscoder.addTranscodingHint(PNGTranscoder.KEY_WIDTH, (float) withd);
imageTranscoder.addTranscodingHint(PNGTranscoder.KEY_HEIGHT, (float) height);
try {
URL imagePath = ImageUtil.class.getResource(resourcePath);
TranscoderInput input = new TranscoderInput(new FileReader(imagePath.getFile()));
imageTranscoder.transcode(input, null);
} catch (FileNotFoundException | TranscoderException e) {
e.printStackTrace();
}
BufferedImage bimage = imageTranscoder.getImage();
WritableImage wimage = SwingFXUtils.toFXImage(bimage, null);
target.setImage(wimage);
}
}
package presentation.utils;
import org.apache.batik.transcoder.TranscoderOutput;
import org.apache.batik.transcoder.image.ImageTranscoder;
import java.awt.image.BufferedImage;
public class SvgTranscoder extends ImageTranscoder {
private BufferedImage image = null;
@Override
public BufferedImage createImage(int w, int h) {
image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
return image;
}
@Override
public void writeImage(BufferedImage img, TranscoderOutput out) {
}
public BufferedImage getImage() {
return image;
}
}
这里也有相同的代码段。 https://snippets.cacher.io/snippet/3dab5b901e6aa5e861e3
我解压缩了jar文件并检查了fxml目录,并包含了文件。 这是屏幕截图。 Unpacked jar files
如果有人对此问题提出建议,我将不胜感激。
答案 0 :(得分:0)
问题是这样的:
new FileReader(imagePath.getFile())
URL名称的getFile()方法尽管命名,但不会返回有效的文件名,并且不会将URL转换为文件。 (该方法是20年前在Java 1.0中引入的,当时大多数URL实际上都表示同一台计算机上或另一台计算机上的物理文件。)
即使存在,.jar文件也只是一个存档,其中的条目本身并不是文件本身,只是代表压缩数据的字节的子序列。
您必须将.jar条目中的资源称为资源URL或其等效流。您不得尝试将其转换为文件。
幸运的是,您不需要文件。您可以将URL作为字符串直接传递:
URL imagePath = ImageUtil.class.getResource(resourcePath);
TranscoderInput input = new TranscoderInput(imagePath.toString());