首页 > 解决方案 > Java:getClass().getResource().toURI() 与 getClass().getResourceAsStream()

问题描述

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

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

打包后我得到:

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

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

我的问题是访问template.xlsfrom module-2.jar

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

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");

我不明白为什么!

这两种方法有什么区别?

标签: javajava-iojava-resources

解决方案


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

an 的概念InputStream要抽象得多。当你打开一个文件进行阅读时,你会得到一个输入流,是的。但是你也可以得到很多其他的输入流:从网络连接读取;读取 zip 文件的解压缩内容。读取解密操作的输出。这个名字真的说明了一切:它是输入,它是一个数据流。这不仅仅适用于“文件系统上的文件”。

Paths.get(template)生成一个路径对象,它代表文件系统上的一个文件。如果template从 URI 派生,则除非您拥有的 URI 恰好是文件对象的 URI,否则这将不起作用;大多数 URI 不用于文件对象。

综上所述,在您的第一个示例中,您在类路径中找到了一个资源(可以是文件,但不一定是。例如,它们可以是 jar 文件中的条目),然后您询问它的 URI,将其提供给 Paths API 以将其转换为 Path 对象,然后要求 Files API 将其转换为 InputStream,这仅在 URI 表示文件时才有效。

在第二个片段中,您只需要求类加载器系统为您获取输入流。它知道如何做到这一点(毕竟,java 必须加载那些类文件!)。如果您要求的资源恰好由文件表示,那么它会在内部执行与您的第一个片段或多或少相同的事情:使用 Files API 打开文件以进行读取。但如果它是其他东西,它也知道如何做到这一点——它还知道如何通过网络从 jar 文件中获取资源,动态生成——类加载的概念(class.getResource这是让你访问的东西)被抽象掉了。

注意:你用错了。正确的方法是ClassYouAreWritingIn.class.getResourceand ClassYouAreWritingIn.class.getResourceAsStream; getClass().getResource是不正确的; 子类化时会中断,而正确的形式不会。


推荐阅读