运行 JAR(Fxyz3d 库)时出现 FileSystemNotFoundException
FileSystemNotFoundException while running JAR (Fxyz3d library)
我构建了一个相当大的 JavaFX 应用程序(JAR 大约 128 MB)并且我在 运行 通过 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(GuiView.java:201)
at com.sun.javafx.application.PlatformImpl.lambda$runLater(PlatformImpl.java:428)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:391)
at com.sun.javafx.application.PlatformImpl.lambda$runLater(PlatformImpl.java:427)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
这仅针对 Fxyz3d 库中的 3D 对象加载器抛出,不会针对我的其他普通 FXML 加载器抛出。我使用相同的方法从我的 src/main/resources 文件夹中获取文件,即 getClass().getResource。
这真的是路径问题吗?还是图书馆的问题?
相反,在 IntelliJ 中完全没有问题,一切正常。
这是代码中不起作用的部分:
Model3D boardModel = Importer3D.loadAsPoly(getClass().getResource("/boardcliff2.obj"));
如果有人以前遇到过类似的事情并且知道它是怎么回事,将不胜感激
José Pereda 打开了 an issue for this which has since been fixed。截至目前(2020 年 8 月 14 日),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:
协议。这意味着来自 ZIP FileSystemProvider 的 FileSystem
,打开到特定 ZIP/JAR 文件,必须存在才能转换为 Path
。如果实现仅使用 URL#openStream()
,则不会发生此问题,它通过不同的机制访问 JAR 条目。
除了 Slaw 的解决方案,我的一位同事找到了替代解决方案:将这些代码行添加到 ObjImporter 的函数 loadAsPoly class(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 库中将必要的 classes 导入到我的 Utils 包中)。
我构建了一个相当大的 JavaFX 应用程序(JAR 大约 128 MB)并且我在 运行 通过 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(GuiView.java:201)
at com.sun.javafx.application.PlatformImpl.lambda$runLater(PlatformImpl.java:428)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:391)
at com.sun.javafx.application.PlatformImpl.lambda$runLater(PlatformImpl.java:427)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
这仅针对 Fxyz3d 库中的 3D 对象加载器抛出,不会针对我的其他普通 FXML 加载器抛出。我使用相同的方法从我的 src/main/resources 文件夹中获取文件,即 getClass().getResource。 这真的是路径问题吗?还是图书馆的问题? 相反,在 IntelliJ 中完全没有问题,一切正常。 这是代码中不起作用的部分:
Model3D boardModel = Importer3D.loadAsPoly(getClass().getResource("/boardcliff2.obj"));
如果有人以前遇到过类似的事情并且知道它是怎么回事,将不胜感激
José Pereda 打开了 an issue for this which has since been fixed。截至目前(2020 年 8 月 14 日),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:
协议。这意味着来自 ZIP FileSystemProvider 的 FileSystem
,打开到特定 ZIP/JAR 文件,必须存在才能转换为 Path
。如果实现仅使用 URL#openStream()
,则不会发生此问题,它通过不同的机制访问 JAR 条目。
除了 Slaw 的解决方案,我的一位同事找到了替代解决方案:将这些代码行添加到 ObjImporter 的函数 loadAsPoly class(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 库中将必要的 classes 导入到我的 Utils 包中)。