Как сообщить Java правильный путь к файлу в JAR-файле?

Я поместил весь свой код 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 также содержит ресурсы. Похоже, что их не хватает. Обычно это может произойти, если вы не запустили mvn compile, так как плагин компиляции также вызывает плагин ресурсов.

José Pereda 31.05.2019 01:43

Вопрос: Если .../computer.png — это ресурс, почему вы пытаетесь прочитать его с FileInputStream?

Slaw 31.05.2019 01:49

@JoséPeda хорошо. Я не звонил mvn compile, потому что это не сработает для меня. Я получаю ошибки, такие как: Main.java:[5,26] package javafx.application does not exist

Daniel Mårtensson 31.05.2019 11:17

@Slaw, потому что я не знаю, как поступить иначе :)

Daniel Mårtensson 31.05.2019 11:18

Посмотрите разделы openjfx.io/openjfx-docs/#IDE-Intellij, Maven и узнайте, как вы можете использовать плагин javafx-maven-plugin для компиляции и запуска вашего проекта. Как только вы заработаете, попробуйте плагин shadow/jar, чтобы получить свою толстую банку.

José Pereda 31.05.2019 11:21

@JoséPeda хорошо! Компиляция работает сейчас. Но есть ли способ создать толстый JAR-файл и запустить его без модулей? Вот так sudo java --module-path = "/usr/share/openjfx/lib" --add-modules=javafx.controls,javafx.fxml -jar JUBSPlotter2-0.0.1-SNAPSHOT-jar-with-dependencies.jar

Daniel Mårtensson 31.05.2019 12:07

@JoséPeda Мне нужно создать модульный проект в maven?

Daniel Mårtensson 31.05.2019 12:10

Толстая банка не работает с модулями, все в пути к классам. Вы можете комбинировать плагин javafx-maven-plugin для компиляции и запуска, а также плагин jar/shade для создания толстой банки. Также, если у вас есть модульный проект (это не обязательно), вы можете использовать jlink (из плагина javafx-maven) для создания образа времени выполнения вместо толстой банки.

José Pereda 31.05.2019 12:19

@JoséPeda Хорошо! Теперь было довольно много информации. Я хочу создать приложение, на которое мне нужно только нажать, а затем оно запустится. Для этого я не должен использовать maven-assembly-plugin? Вместо этого я должен использовать mvn javafx:jlink для создания исполняемого образа?

Daniel Mårtensson 31.05.2019 12:24

Посмотрите этот отвечать и не торопитесь его обрабатывать :)

José Pereda 31.05.2019 12:26

@JoséPeda Похоже, мне нужно следовать руководству Modular по вашей URL-ссылке openjfx :)

Daniel Mårtensson 31.05.2019 12:41

@JoséPeda Привет! Я получил ошибку Could not find goal 'jlink' in plugin org.openjfx:javafx-maven-plugin:0.0.1 Это потому, что я запускаю OpenJDK 11?

Daniel Mårtensson 31.05.2019 12:55

Увеличьте версию плагина до 0.0.2

José Pereda 31.05.2019 12:57

@JoséPeda Стремись к успеху! Это должно быть в руководстве, потому что 0.0.1 по умолчанию.

Daniel Mårtensson 31.05.2019 13:00

То есть в мануале указано JavaFX 12 и плагин 0.0.2, см. например этот рис.

José Pereda 31.05.2019 13:04

@JoséPeda Хорошо. Спасибо. Когда я запускаю mvn javafx:jlink и мой проект называется JUSBPlotter, я ожидаю папку с именем JUSBPlotter внутри папки target, но не могу найти папки с именем JUSBPlotter. Или это папка image, которую я должен запустить?

Daniel Mårtensson 31.05.2019 13:07

@JoséPeda Теперь это работает. Я просто следую этому руководству, чтобы создать образ! github.com/openjfx/javafx-maven-плагин JavaFX великолепен!

Daniel Mårtensson 31.05.2019 13:31
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
17
496
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Следует помнить, что ресурсы — это не то же самое, что файлы, несмотря на то, что они кажутся очень похожими, особенно перед упаковкой приложения. Когда вы упаковываете приложение, эти файлы ресурсов становятся записями в JAR-файле, который, хотя и может рассматриваться как файлы, не может быть доступен так же, как файлы. В частности, java.io.File API не понимает, как читать эти записи. Однако вы можете получить доступ к этим записям через URL, возвращаемый Class#getResource(String), или InputStream, возвращаемый Class#getResourceAsStream(String). Обратите внимание, что последний в основном просто находит URL и, если он существует, вызывает URL#openStream(). Причина, по которой вы можете получить доступ к записям JAR (т.е. ресурсам) с помощью URL, связана с JarURLConnection и связанными классами; Я не буду вдаваться в подробности о том, как Java обрабатывает URL-адреса, поскольку это выходит за рамки этого ответа.

Теперь, поскольку вы пытаетесь отобразить изображение в JavaFX, я предполагаю, что вы используете класс Image. Есть три основных способа сообщить Image, где находится ресурс изображения.

  1. Используйте Image#<init>(String), если URL-адрес аргумента не имеет схемы. В этом случае класс будет рассматривать URL-адрес как путь относительно корня пути к классам и попытается соответствующим образом получить доступ к изображению. По сути, аргумент — это тот же аргумент, который вы бы дали Class#getResource(String), за исключением того, что путь должен быть абсолютным (то есть не относительным ни к какому классу). Это поведение задокументировано классом Image:

    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.

    Примечание. Если вы используете модули, то при использовании этой опции в игру вступает инкапсуляция ресурсов. См. Ошибка GitHub № 441.

  2. Используйте тот же конструктор, что и выше, но передайте действительный URL-адрес (т.е. он имеет схему). При использовании ресурса вы можете просто преобразовать URL, возвращаемый Class#getResource(Sring), в String, используя URL#toExternalForm().

    Image image = new Image(getClass().getResource(...).toExternalForm());
    

    Примечание. Вы также можете использовать URL#toString(), который возвращает то же значение.

  3. Используйте Image#<init>(InputStream). Здесь вы можете использовать Class#getResourceAsStream(String). Не забудьте закрыть InputStream, когда закончите с этим.

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

Все это предполагает, что ресурсы были правильно упакованы в ваш файл JAR, когда вы его создавали. Если это не так, сначала следуйте совету Хосе Переды в комментариях к вопросу.


Обратите внимание, что доступ к записям файла ZIP/JAR можно получить с помощью java.util.zip.* API, java.util.jar.* API или Java NIO ZipFileSystemProvider. Каждый из них, особенно последний, позволяет вам получить доступ к записям ZIP/JAR-файла, аналогичного реальным файлам, потому что они разработаны специально для этого. Однако при использовании ресурсов в приложении вы должны придерживаться Class#getResource(String) и связанных с ним методов.

Другие вопросы по теме