【问题标题】:Java access files in jar causes java.nio.file.FileSystemNotFoundExceptionjar 中的 Java 访问文件导致 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 stackoverflow.com/questions/5171957/access-file-in-jar-file 你必须做些不同的事情
  • 答案中使用的方法读取单个文件为Stream,我想复制整个目录。我添加了一个屏幕截图来澄清
  • 也许你也想用Files.createTempDirectory
  • 是的,我在我的 main() 某处这样做了
  • 在 IDE (Eclipse) 中运行时,资源实际上是一个文件。如果您打包应用程序并尝试在 IDE 之外运行它,问题就会浮出水面。

标签: java java-7 nio


【解决方案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?
猜你喜欢
  • 2011-08-12
  • 1970-01-01
  • 2016-09-30
  • 2011-07-07
  • 1970-01-01
  • 2011-01-15
  • 2016-04-24
  • 2013-12-13
  • 1970-01-01
相关资源
最近更新 更多