Java Jar文件:使用资源错误:URI不是分层结构的

88

我已经将我的应用程序部署到jar文件中。当我需要将资源文件中的数据复制到jar文件之外时,我使用以下代码:

URL resourceUrl = getClass().getResource("/resource/data.sav");
File src = new File(resourceUrl.toURI()); //ERROR HERE
File dst = new File(CurrentPath()+"data.sav");  //CurrentPath: path of jar file don't include jar file name
FileInputStream in = new FileInputStream(src);
FileOutputStream out = new FileOutputStream(dst);
 // some excute code here

我遇到的错误是:URI不是分层的。但在IDE中运行时,我并没有遇到此错误。
如果根据StackOverFlow上其他帖子的建议更改上述代码:
InputStream in = Model.class.getClassLoader().getResourceAsStream("/resource/data.sav");
File dst = new File(CurrentPath() + "data.sav");
FileOutputStream out = new FileOutputStream(dst);
//....
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0) { //NULL POINTER EXCEPTION
  //....
}
6个回答

126

你不能这样做。

File src = new File(resourceUrl.toURI()); //ERROR HERE

这不是一个文件!当您从IDE运行时,不会出现任何错误,因为您不会运行JAR文件。在IDE中,类和资源都被提取到文件系统中。

但您可以通过以下方式打开InputStream

InputStream in = Model.class.getClassLoader().getResourceAsStream("/data.sav");

移除"/resource"。一般来说,IDE会分离文件系统类和资源。但是当JAR文件被创建时,它们都被放在一起。因此,目录级别的"/resource"仅用于类和资源的分离。

当您从类加载器获取资源时,必须指定资源在JAR文件中的路径,即真实的包层次结构。


2
请发布您的项目层级结构。 - dash1e
24
如果我确实需要一个“文件”对象怎么办? - TekTimmy
2
@Daniel 这取决于您放置资源的位置。如果您将资源文件放在类文件的同一包中,则可以使用 data.sav 而不需要起始 /。但是在问题中,data.sav 在根包中,因此在这种情况下,他需要在前面加上 / - dash1e
4
就像TekTimmy所说:但如果我真的需要一个文件怎么办?我正在进行电子邮件发送过程中,我们想要将一个logo文件图片加入到电子邮件中。这张图片存在于jar文件中,我必须将其作为文件而不是InputStream提供给数据源... - JN Gerbaux
2
@JNGerbaux 我也想要一个 File 对象,所以我创建了一个临时的 File 并将内容从 InputStream 复制到 File 对象中。使用 FileUtils.copyInputStreamToFile(inputStream, file); - Vishal Patel
显示剩余7条评论

29

如果出于某种原因你确实需要创建一个 java.io.File 对象来指向 Jar 文件内的资源,答案在这里:https://dev59.com/inNA5IYBdhLWcg3wfN6O#27149287

File f = new File(getClass().getResource("/MyResource").toExternalForm());

11

以下是 Eclipse RCP / Plugin 开发者的解决方案:

Bundle bundle = Platform.getBundle("resource_from_some_plugin");
URL fileURL = bundle.getEntry("files/test.txt");
File file = null;
try {
   URL resolvedFileURL = FileLocator.toFileURL(fileURL);

   // We need to use the 3-arg constructor of URI in order to properly escape file system chars
   URI resolvedURI = new URI(resolvedFileURL.getProtocol(), resolvedFileURL.getPath(), null);
   File file = new File(resolvedURI);
} catch (URISyntaxException e1) {
    e1.printStackTrace();
} catch (IOException e1) {
    e1.printStackTrace();
}

使用FileLocator.toFileURL(fileURL)而不是resolve(fileURL)非常重要,因为当插件被打包成jar文件时,这将导致Eclipse在临时位置创建一个未打包的版本,以便可以使用File访问该对象。例如,我猜Lars Vogel在他的文章中有一个错误 - http://blog.vogella.com/2010/07/06/reading-resources-from-plugin/


1
什么是平台类?需要一些非实时库吗? - Andrey
2
@AndreyP 这个解决方案是针对Eclipse RCP插件开发人员的。我建议尝试使用getResourceAsStream(...)方法。 - Ilya Buziuk
1
在我的使用Maven构建的RCP E4应用程序中,需要提取和打开jar捆绑的二进制文件。您的解决方案最终使我能够这样做,并在操作系统默认浏览器中打开file.getAbsolutePath() - J. Katzwinkel
谢谢!这个答案非常有帮助,因为我在使用FileLocator.resolve时遇到了一个bug。我可以直接创建File对象: File f = new File(FileLocator.toFileURL(resolvedFileURL).toURI()); - Rémi

1

我之前遇到过类似的问题,我使用了以下代码:

new File(new URI(url.toString().replace(" ","%20")).getSchemeSpecificPart());

代替代码的是:
new File(new URI(url.toURI())

解决问题


0
除了一般的答案外,您还可以从Unitils库中获取“URI不是分层结构”,尝试从.jar文件加载数据集。当您将数据集保存在一个Maven子模块中,但实际测试在另一个子模块中时,可能会发生这种情况。
甚至有一个bug UNI-197被提交。

0

虽然我自己遇到了这个问题,但我想再添加一个选项(除了@dash1e的完美解释):

通过添加以下内容将插件导出为文件夹(而不是jar):

Eclipse-BundleShape: dir

添加到你的MANIFEST.MF文件中。

至少当你使用基于*.product文件的导出向导导出你的RCP应用程序时,这将被尊重并生成一个文件夹。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接