从jar里面获取资源文件

Getting resource file from inside jar

当应用程序打包到 jar 中时,我需要从应用程序的根目录中获取资源。我的项目是这样的:

ProjectRoot
  resource.txt //want to access from here
  src
    main
       java
         package1
           package2
              package3
                 Main.java
  target
    app.jar
    classes
         resource.txt //works when here
         package1
           package2
              package3
                 Main.class

我使用这个代码:

Path path = Paths.get("resource.txt");

当 运行 打包成 jar 之前,它发现文件很好(在 ProjectRoot 中)。 运行ning jar 时找不到,将此路径转换为target/resource.txt.

此代码:

BufferedReader br = new BufferedReader(new InputStreamReader(new Main().getClass().getClassLoader().getResourceAsStream(
                "resource.txt")));

当运行打包前找target/classes里面的资源。打包后它声称从 .../target/app.jar!/resource.txt.

获取资源

此代码:

BufferedReader br = new BufferedReader(new InputStreamReader(new Main().getClass().getClassLoader().getResourceAsStream(
                    "/resource.txt")));

我不明白在哪里寻找资源,但它似乎不是ProjectRoot

我想要做的就是将资源放在 ProjectRoot 中,并能够从两个外部 jar 访问它(当 运行 从 [=41] 中获取 class 文件时=]) 和内部(使用 Maven 将文件打包成 jar 文件后)。

编辑:我需要代码来处理预打包和 POST- 打包。含义:如果我 运行 a Main.java FROM INSIDE IDE 它将获得资源;如果我将所有内容打包到 JAR 中,然后 运行 JAR 它将获得资源 - 全部使用相同的代码。

使用:Main.class.getResource("/resource.txt").

请注意,您尝试使用对 getClassLoader 的任何调用都非常糟糕(它更多的文本,并且会更频繁地失败,因为 class 加载程序在特殊情况下可以为空(具体来说,当你是 bootstrap 加载程序的一部分),而直接在 class 上调用 getResource 始终有效。

您的代码段不起作用的原因是,在 class 加载程序上调用 getResource 时,您不得以斜线开头资源。当直接调用 class 时,你可以(如果你不这样做,它将相对于你正在调用它的 class 的包,如果你这样做,它将是相对于根)。

TL;DR: 在 SomeClass.class.getClassLoader().getResourcegetClass().getResourceMyClass.class.getResource 形式中,只有最后一个是正确的,其余的都是次等的,因此根本不应该使用.

Maven 使用了一种叫做 Standard Directory Layout 的东西。如果您不遵循此布局,那么插件将无法正常工作。从技术上讲,您可以将 Maven 配置为使用不同的目录,但在 99.999% 的情况下,这是没有必要的。

此布局的特点之一是生产文件进入:

  • <project-dir>/src/main/java
    • 所有 *.java 个文件
  • <project-dir>/src/main/resources
    • 所有非*.java 文件(即资源)

当您构建项目时,会编译 Java 源文件并将 *.class 文件放入 target/classes 目录;这是由 maven-compiler-plugin 完成的。同时,资源文件也从src/main/resources复制target/classesmaven-resources-plugin 对此负责。

注:Introduction to the Build Lifecycle for more information about phases and which plugins are executed by which phase. This Stack Overflow question可能也有用。

当您从 IDE 启动您的应用程序时(可能通过 exec-maven-plugintarget/classes 目录被放置在类路径中。这意味着来自 src/main/java 的所有已编译 类 和来自 src/main/resources 的所有复制资源都可以通过类路径使用。

然后,当您将应用程序打包到 JAR 文件中时,target/classes 中的所有文件都会添加到生成的 JAR 文件中(由 maven-jar-plugin 处理)。 这包括从 src/main/resources 复制的资源。当您使用此 JAR 文件启动应用程序时,资源仍可通过类路径使用,因为它们嵌入在 JAR 文件中。

要使 resource.txt 在类路径上可用,只需移动:

<project-dir>/resource.txt

收件人:

<project-dir>/src/main/resources/resource.txt.

然后您可以使用 Class#getResource/resource.txt 作为路径,一切都会为您解决。 getResource 返回的 URL 会有所不同,具体取决于您是针对 target/classes 还是针对 JAR 文件执行。

当针对 target/classes 执行时,您会得到如下内容:

file:///.../<project-dir>/target/classes/resource.txt

当针对 JAR 文件执行时,您会得到如下内容:

jar:file:///.../<project-dir>/target/projectname-version.jar!/resource.txt

注意: 这一切都假定 resource.txt 实际上应该是资源而不是外部文件。一旦部署在 JAR 文件中,资源通常 read-only;如果您需要一个可写文件,那么您可以使用该文件的指定位置(例如,用户主目录中的文件夹)。人们通常通过 java.io.Filejava.nio.file.* 访问外部文件。请记住,资源与普通文件不同。

现在,如果您将 resource.txt 直接放在 <project-dir> 下,那么 对 Maven 毫无意义。它不会被复制到 target/classes 或最终出现在 JAR 文件中,这意味着该资源在类路径上永远不可用。所以重申一下,所有资源都在 src/main/resources.


查看 java.lang.Class#getResource(String) 的 Java 文档以获取有关路径的更多信息,例如何时使用前导 / 以及何时不使用。 link 指向 Javadoc for Java 12,其中包括有关资源和模块的信息(JPMS/Jigsaw 模块,而不是 Maven 模块);如果您不使用模块,则可以忽略文档的那一部分。