Java: getClass().getResource().toURI() Vs getClass().getResourceAsStream()

huangapple go评论76阅读模式
英文:

Java : getClass().getResource().toURI() Vs getClass().getResourceAsStream()

问题

以下是翻译好的内容:

我有一个使用java编写的多模块sbt项目,其中一些模块包含一个resources文件夹。

模块-1
  resources
    template.xls
模块-2
  resources
    other.xls

在打包后,我得到了:

lib/module-1.jar
lib/module-2.jar

我像运行其他Java应用程序一样运行我的程序:java -cp "lib/*" MainClass

我的问题是如何从module-2.jar访问template.xls

起初,我尝试了以下几行代码来获取我的模板:

URI template = getClass().getResource("/template.xls").toURI();
Files.newInputStream(Paths.get(template), StandardOpenOption.READ);

开发模式下,它是有效的。但是在服务器上(部署后),它找不到资源。

java.nio.file.FileSystemNotFoundException: null
[jar:file:/.../lib/module-1.jar!/template.xls]

经过一些研究,我修改了我的访问代码,如下所示,以使其在两种模式下(开发和部署)都能正常工作:

InputStream templateIS = getClass().getResourceAsStream("/template.xls");

我不明白为什么!

这两种方法之间有什么区别?

英文:

I have a java multiple modules sbt project and some of them contains a resources folder.

module-1
  resources
    template.xls
module-2
  resources
    other.xls

After packaging I got :

lib/module-1.jar
lib/module-2.jar

And I run my program like any other java application : java -cp "lib/*" MainClass.

My problem is accessing the template.xls from module-2.jar.

At first, I've tried the lines below to get my template :

URI template = getClass().getResource("/template.xls").toURI();
Files.newInputStream(Paths.get(template), StandardOpenOption.READ);

In development mode it works. But not on the server (after deployment), it cannot find the resource.

> java.nio.file.FileSystemNotFoundException: null
> [jar:file:/.../lib/module-1.jar!/template.xls]

After some research I modified my accessing code like the following to get it works in both modes (development and deployed) :

InputStream templateIS = getClass().getResourceAsStream("/template.xls");

I cannot understand why !

What is the difference between the two methods ?

答案1

得分: 3

Files.newInputStream,顾名思义,可以打开文件。它不能打开其他任何东西。它只能用于文件。

InputStream 的概念要抽象得多。当你打开一个文件进行读取时,你会得到一个输入流,是的。但你也可以得到许多其他东西的输入流:从网络连接中读取;从zip文件的解压内容中读取。读取解密操作的输出。名字已经说明了一切:这是输入,是一串数据的流。这适用于比“文件在文件系统上”要多得多的情况。

Paths.get(template) 会生成一个路径对象,代表文件系统上的一个文件。如果 template 是来自于一个URI,除非这个URI碰巧是一个指向文件对象的URI,否则这是行不通的;大多数URI都不是指向文件对象的。

把这一切都放在一起,在你的第一个示例中,你在类路径上找到一个资源(可以是文件,但不一定是文件。例如,它们可以是jar文件中的条目),然后获取它的URI,将其传递给Paths API将其转换为Path对象,然后再将这个Path对象传递给Files API将其转换为InputStream,前提是URI代表一个文件。

在第二个代码片段中,你只是请求类加载系统为你获取一个输入流。它知道如何做到这一点(毕竟,Java必须加载那些类文件!)。如果你要请求的资源碰巧由一个文件表示,它会在内部执行与第一个代码片段几乎相同的操作:使用Files API打开文件进行读取。但如果是其他任何东西,它也知道如何处理 —— 它也知道如何从网络中获取资源,从jar文件中获取资源,或者动态生成资源 —— 类加载的概念(这就是class.getResource让你访问的内容)被抽象化了。

注意:你使用的方法不正确。正确的方式是 ClassYouAreWritingIn.class.getResourceClassYouAreWritingIn.class.getResourceAsStreamgetClass().getResource 是不正确的;那种方式在子类化时会出问题,而正确的形式则不会出问题。

英文:

Files.newInputStream, as the name suggests, can open files. It cannot open anything else. It's just for files.

The concept of an InputStream is much more abstract. When you open a file for reading, you get an inputstream, yup. But you also get inputstreams for many other things: Reading from a network connection; reading the unpacked contents of zip files. Reading the output of a decryption operation. The name says it all really: It's input, and it's a stream of data. Which applies to so much more than 'file on a filesystem'.

Paths.get(template) produces a path object, which represents a file on the file system. If template is derived from a URI, this does not work unless the URI that you have so happens to be a URI to a file object; most URIs are not to file objects.

Putting it all together, in your first sample, you find a resource on the classpath (which can be files, but don't have to be. For example, they could be entries in a jar file), you then ask its URI, feed it to the Paths API to turn it into a Path object, and then ask the Files API to turn this into an InputStream, which only works if the URI represents a file.

In the second snippet, you just ask the classloader system to get you an inputstream. It knows how to do that (after all, java has to load those class files!). If the resource you're asking for so happens to be represented by a file, it's going to do, internally, more or less the same thing as your first snippet: Use the Files API to open the file for reading. But if it's anything else, it knows how to do that too – it also knows how to get resources across a network, from inside jar files, generated on-the-fly – the concept of class loading (which is what class.getResource lets you access) is abstracted away.

NB: You're using it wrong. The proper way is ClassYouAreWritingIn.class.getResource and ClassYouAreWritingIn.class.getResourceAsStream; getClass().getResource is not correct; that breaks when subclassing, whereas the correct form doesn't.

答案2

得分: 2

你的第一个情况不起作用,因为template.xls被打包在一个jar文件内。它不是文件系统上的文件(而在打包之前很可能在您的开发环境中是一个文件)。因此,Files API 无法找到它。

Class.getResourceAsStream()利用了类加载机制,而这个类(显然)是从.jar文件中加载的。

英文:

Your first scenario doesn't work because the template.xls is packaged within a jar file. It's not a file on the filesystem (whereas it likely is in your development env prior to packaging as a file). As such the Files API can't find it.

Class.getResourceAsStream() makes use of the classloading mechanism, and the class (obviously) is loaded from the .jar file.

答案3

得分: 2

文档中,

方法getResource()返回资源的URL。URL(及其表示)是特定于实现和JVM的(即在一个运行时实例中获得的URL可能在另一个实例中无法工作)。它的协议通常特定于加载资源的ClassLoader。如果资源不存在或由于安全考虑不可见,则方法返回null。

如果客户端代码想要将资源的内容作为InputStream读取,它可以在URL上应用openStream()方法。这种情况足够常见,可以理解为在Class和ClassLoader中添加getResourceAsStream()。getResourceAsStream()与调用getResource().openStream()相同,只是getResourceAsStream()捕获IO异常并返回null InputStream。

因此,getResourceAsStream()与调用getResource().openStream()相同,只是getResourceAsStream()捕获IO异常并返回null InputStream。

英文:

From docs,

> The method getResource() returns a URL for the resource. The URL (and
> its representation) is specific to the implementation and the JVM
> (that is, the URL obtained in one runtime instance may not work in
> another). Its protocol is usually specific to the ClassLoader loading
> the resource. If the resource does not exist or is not visible due to
> security considerations, the methods return null.
>
> If the client code wants to read the contents of the resource as an
> InputStream, it can apply the openStream() method on the URL. This is
> common enough to justify adding getResourceAsStream() to Class and
> ClassLoader. getResourceAsStream() the same as calling
> getResource().openStream(), except that getResourceAsStream() catches
> IO exceptions returns a null InputStream.

So, getResourceAsStream() the same as calling getResource().openStream(), except that getResourceAsStream() catches IO exceptions returns a null InputStream.

huangapple
  • 本文由 发表于 2020年4月9日 18:51:23
  • 转载请务必保留本文链接:https://go.coder-hub.com/61119462.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定