jar 中的 Java 访问文件导致 java.nio.file.FileSystemNotFoundException

Posted

技术标签:

【中文标题】jar 中的 Java 访问文件导致 java.nio.file.FileSystemNotFoundException【英文标题】:Java access files in jar causes java.nio.file.FileSystemNotFoundException 【发布时间】:2014-05-01 14:08:24 【问题描述】:

在尝试使用我的 java 应用程序将我的 jar 文件中的一些文件复制到临时目录时,抛出以下异常:

java.nio.file.FileSystemNotFoundException
    at com.sun.nio.zipfs.ZipFileSystemProvider.getFileSystem(ZipFileSystemProvider.java:171)
    at com.sun.nio.zipfs.ZipFileSystemProvider.getPath(ZipFileSystemProvider.java:157)
    at java.nio.file.Paths.get(Unknown Source)
    at com.sora.util.walltoggle.pro.WebViewPresentation.setupTempFiles(WebViewPresentation.java:83)
   ....

这是我setupTempFiles(带行号)的一小部分:

81. URI uri = getClass().getResource("/webViewPresentation").toURI();
//prints: URI->jar:file:/C:/Users/Tom/Dropbox/WallTogglePro.jar!/webViewPresentation
82. System.out.println("URI->" + uri );
83. Path source = Paths.get(uri);

webViewPresentation 目录位于我的 jar 的根目录中:

只有当我将我的应用程序打包为 jar 时才会出现此问题,在 Eclipse 中调试没有问题。我怀疑这与bug 有关,但我不确定如何解决此问题。

任何帮助表示赞赏

如果重要的话:

我正在使用 Java 8 build 1.8.0-b132

Windows 7 Ult. x64

【问题讨论】:

对于 jars ***.com/questions/5171957/access-file-in-jar-file 你必须做些不同的事情 答案中使用的方法读取单个文件为Stream,我想复制整个目录。我添加了一个屏幕截图来澄清 也许你也想用Files.createTempDirectory 是的,我在我的 main() 某处这样做了 在 IDE (Eclipse) 中运行时,资源实际上是一个文件。如果您打包应用程序并尝试在 IDE 之外运行它,问题就会浮出水面。 【参考方案1】:

这可能是一个 hack,但以下对我有用:

URI uri = getClass().getResource("myresourcefile.txt").toURI();

if("jar".equals(uri.getScheme()))
    for (FileSystemProvider provider: FileSystemProvider.installedProviders()) 
        if (provider.getScheme().equalsIgnoreCase("jar")) 
            try 
                provider.getFileSystem(uri);
             catch (FileSystemNotFoundException e) 
                // in this case we need to initialize it first:
                provider.newFileSystem(uri, Collections.emptyMap());
            
        
    

Path source = Paths.get(uri);

这使用了 ZipFileSystemProvider 在内部存储由 URI 打开的文件系统列表的事实。

【讨论】:

奇怪的是Paths.get(URI) 没有处理jar 文件路径的能力...【参考方案2】:

接受的答案不是最好的,因为当您在 IDE 中启动应用程序或资源是静态的并存储在类中时它不起作用! java.nio.file.FileSystemNotFoundException when getting file from resources folder提出了更好的解决方案

InputStream in = getClass().getResourceAsStream("/webViewPresentation");
byte[] data = IOUtils.toByteArray(in);

IOUtils 来自 Apache commons-io。

但是如果你已经在使用 Spring 并且想要一个文本文件,你可以将第二行更改为

StreamUtils.copyToString(in, Charset.defaultCharset());

StreamUtils.copyToByteArray 也存在。

【讨论】:

OP 试图获取Path(可能是为了 NIO 中新的异步处理能力),而不是流或字符串,所以这不是答案。 它可能不是问题的答案,但确实 path/uri 解决方案无法使用,原因有两个:如果资源被解包,它不起作用,如果您从jar 同时 - 您只能打开一次文件系统......但是一般来说,您如何知道它是否是同一个文件系统?很多测试是必要的。这里的答案显示了一个简单而有效的解决方案。【参考方案3】:

如果您使用的是 spring 框架库,那么有一个简单的解决方案。

根据要求,我们要阅读 webViewPresentation; 我可以用下面的代码解决同样的问题:

URI uri = getClass().getResource("/webViewPresentation").toURI();
FileSystems.getDefault().getPath(new UrlResource(uri).toString());

【讨论】:

似乎不适用于嵌套 JAR 或 BOOT-INF/classes【参考方案4】:

FileSystemNotFoundException 表示无法自动创建文件系统;而且你还没有在这里创建它。

根据你的 URI,你应该做的是拆分 !,使用它之前的部分打开文件系统,然后从 ! 之后的部分获取路径:

final Map<String, String> env = new HashMap<>();
final String[] array = uri.toString().split("!");
final FileSystem fs = FileSystems.newFileSystem(URI.create(array[0]), env);
final Path path = fs.getPath(array[1]);

请注意,完成后您应该 .close() 您的 FileSystem

【讨论】:

是的,这不是很好的文档记录...如果这些路径不是来自同一个文件系统提供程序,您应该 .resolve().relativize() 其他路径的 .toString() 值。 .. 我也被它所困扰(我正在通过 FTP 开发文件系统实现) 不确定它是否更好,但here you go :) 请注意,从那时起我开发了一些您可能感兴趣的东西;见here @8odoros 你最好使用 .replaceFirst();您只想替换找到的第一个匹配项,而不是全部替换 这种方法可以使用嵌套的 jar,例如jar:file:outer.jar!/inner.jar!/file.txt?

以上是关于jar 中的 Java 访问文件导致 java.nio.file.FileSystemNotFoundException的主要内容,如果未能解决你的问题,请参考以下文章

jar报错红线项目能正常启动,jar不报错项目不能启动

解决在eclipse2021中,用mysql-connector-java-8.0.18.jar不兼容,导致无法访问数据库问题(2021-10-23)

如何访问位于源文件夹中的 Jar 文件?

错误记录Flutter 混合开发报错 ( java.nio.file.FileSystemException: xxx/R.jar: 另一个程序正在使用此文件,进程无法访问。 )

从 Apache Spark Streaming 上下文访问 JAR 中资源目录中的文件

Java如何将dll文件打包到jar中?