FileSystemNotFoundException при запуске JAR (библиотека Fxyz3d)

Я создал довольно толстое приложение JavaFX (размер JAR около 128 МБ), и у меня не возникло проблем с запуском через IntelliJ. Но когда я запускаю его с терминала, мои загрузчики 3D-моделей (библиотека Fxyz3d) запускают это исключение.

Exception in thread "JavaFX Application Thread" java.nio.file.FileSystemNotFoundException
    at jdk.zipfs/jdk.nio.zipfs.ZipFileSystemProvider.getFileSystem(ZipFileSystemProvider.java:172)
    at jdk.zipfs/jdk.nio.zipfs.ZipFileSystemProvider.getPath(ZipFileSystemProvider.java:158)
    at java.base/java.nio.file.Path.of(Path.java:208)
    at java.base/java.nio.file.Paths.get(Paths.java:98)
    at org.fxyz3d.importers.obj.ObjImporter.read(ObjImporter.java:115)
    at org.fxyz3d.importers.obj.ObjImporter.loadAsPoly(ObjImporter.java:102)
    at org.fxyz3d.importers.Importer3D.loadIncludingAnimation(Importer3D.java:160)
    at org.fxyz3d.importers.Importer3D.loadAsPoly(Importer3D.java:80)
    at it.polimi.ingsw.PSP50.View.GUI.GuiView.lambda$startingGame$1(GuiView.java:201)
    at com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:428)
    at java.base/java.security.AccessController.doPrivileged(AccessController.java:391)
    at com.sun.javafx.application.PlatformImpl.lambda$runLater$11(PlatformImpl.java:427)
    at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96) 

Это выбрасывается только для Загрузчика 3D-объектов из библиотеки Fxyz3d, а не для других моих обычных загрузчиков FXML. Я использую тот же способ получить файлы из моей папки src/main/resources, то есть getClass(). GetResource. Так действительно ли это проблема пути? или это проблема библиотеки? В IntelliJ проблем нет, все работает нормально. Вот часть кода, которая не работает:

Model3D boardModel = Importer3D.loadAsPoly(getClass().getResource("/boardcliff2.obj"));
 

Если кто-то сталкивался с чем-то подобным раньше и знает, что происходит, помощь будет очень признательна

2 ответа

Решение

Хосе Переда открыл для этого проблему, которая с тех пор исправлена. На данный момент (14 августа 2020 г.) последняя версия FXyz - 0.5.2, которая не включает исправление этой проблемы. Вы можете продолжать использовать обходной путь, показанный в этом ответе, самостоятельно создать библиотеку из самого последнего коммита или дождаться следующего выпуска библиотеки.


Похоже, это проблема с реализацией. Он пытается преобразоватьURL в Path но необходимый FileSystemне существует1. Лучшим обходным решением, вероятно, является извлечение ресурса во временный файл, а затем импорт объекта из указанного файла. Таким образом, URL-адрес будет иметьfile: протокол и преобразование в Path будет работать (по умолчанию FileSystemвсегда существует). Вот пример того, как можно извлечь ресурс:

// Note: 'Path' is 'java.nio.file.Path', not 'javafx.scene.shape.Path'
public static Path copyToTempFile(URL url, String suffix) throws IOException {
  // 'suffix' will default to ".tmp" if null
  Path tempFile = Files.createTempFile(null, suffix);
  try (InputStream in = url.openStream();
       OutputStream out = Files.newOutputStream(tempFile)) {
    in.transferTo(out); // 'transferTo' method added in Java 9
  }
  return tempFile;
}

Затем вы можете использовать полученный Path чтобы импортировать 3D-объект:

Path tempFile = copyToTempFile(getClass().getResource("/boardcliff2.obj"), ".obj");
Model3D boardModel = Importer3D.loadAsPoly(tempFile.toUri().toURL());

При желании вы можете удалить временный файл, когда закончите с ним.


1. При внедрении в файл JAR URL ресурса имеет jar:протокол. Это означаетFileSystemиз ZIP FileSystemProvider, открытого в конкретный файл ZIP/JAR, должен существовать для преобразования вPathработать. Эта проблема не возникла бы, если бы реализация просто использовалаURL#openStream(), который обращается к записи JAR через другой механизм.

Помимо решения Slaw, мой коллега нашел альтернативное решение: добавление этих строк кода в функцию loadAsPoly из класса ObjImporter (библиотека Fxyz3d) устраняет проблему для каждого импорта.

public class ObjImporter implements Importer {
    ...
    @Override
    public Model3D loadAsPoly(URL url) throws IOException {
        // Additional lines
             if(url.getProtocol().equals("jar")) {
                            try {
                                Map<String, String> env = new HashMap<>();
                                env.put("create", "true");
                                FileSystem zipfs = FileSystems.newFileSystem(url.toURI(), env);
                            } catch(FileSystemAlreadyExistsException ignored) {

                            } catch (IOException | URISyntaxException e) {
                                e.printStackTrace();
                            }
                        }
        // End of the addition
        return read(url, true);
    }
   ...
}

Я тестировал его с помощью Jar, и он тоже работает (конечно, в моем проекте я импортировал необходимые классы из библиотеки Fxyz в свой пакет Utils).

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