【问题标题】:Retrieving resources for a subpackage class检索子包类的资源
【发布时间】:2015-06-07 10:52:24
【问题描述】:

我使用 java 的时间不长,所以我不确定还要寻找什么。我希望有人能指出我正确的方向。

目标: 我想使用一个查找表,存储为文本文件。但我不想使用绝对路径,因为最后我想打包一个版本并能够从任何位置调用它(文本文件将包含在打包版本中)。

当前设置: 我将文本文件放在一个名为“resources”的文件夹中(因为通过阅读有关 java 的教程,我得到了一个印象,我应该把它放在这个位置以维护一个更好的结构化项目)。

在根包文件夹中,我有一个类 (MainClass.java),它调用子包中的另一个类 (LookUpClass.java)。 文件夹设置如下:

    • Java
      • main.package.com
        • 分包
          • Lo​​okUpClass.java
          • PlotterClass.java
        • MainClass.java
    • 资源
      • 查找表
        • Lo​​okUpTable1.txt
        • Lo​​okUpTable2.txt

我在LookUpClass.java 中编写了一个方法,该方法从我的资源查找表中检索某一行。为了检索文件并读出某一行,我使用了

// Gets respective line from LUT
private static String getLineFromLUT(int line) {
    URL url = LookUpClass.class.getClass().getResource("/LookUpTables/LookUpTable1.txt");
    File file = new File(url.toURI());
    BufferedReader br = new BufferedReader(new FileReader(file));

    for (int i = 0; i < line; ++i)
        br.readLine();

    return br.readLine;
}

在我的项目结构中,“java”文件夹被标记为“source”,而“resources”被标记为“resources”。

我的测试设置非常简单:

public static void main(String[] args) throws URISyntaxException, IOException {
    String c = LookUpClass.getLineFromLUT(5);
    System.out.println("Color of line 5: " + c);
}

输出:

Color of line 5: 0  0   38

(这是正确的。)

我在PlotterClass.java 中添加了完全相同的行,它也可以正常工作。

问题:

现在,如果我在 MainClass.java 中尝试相同的操作,我会收到 url 为空的错误。似乎找不到资源/资源文件夹。

我已经阅读了关于 SO 的各种帖子,并尝试了几个建议的解决方案,但到目前为止都失败了:

  • 如果使用LookUpClass.class.getClassLoader().getResource("/LookUpTables/LookUpTable1.txt") 来自MainClass.javaLookUpClass.java 的调用都失败(url 为空)。
  • 我尝试使用以下路径(在任何一个类中都不起作用): “LookUpTables/LookUpTable1.txt”(删除起始“/”) “/subpackage/LookUpTables/LookUpTable1.txt” "../subpackage/LookUpTables/LookUpTable1.txt"
  • 由于使用了 Idea IntelliJ,我检查了“设置 > 构建、执行、部署 > 编译器 > 资源模式”并将“*.txt”添加到模式中。没有任何改变。
  • 如果添加Class c = LookUpClass.class.getClass();,在Debug模式下c是“class.java.lang.Class”。我期待像“main.package.com.subpackage.LookUpClass”这样的东西。
  • 在某些时候我尝试使用getResourceAsStream(),但我不明白如何获得我的(例如)第 5 行,所以我放弃了它。如果它解决了我的问题,我愿意阅读此内容。

我不知道如何解决这个问题。我意识到在这一点上我只是在尝试一些东西,甚至不明白为什么它可以或不能工作。 对我来说,似乎LookUpClass.java 的运行位置与MainClass.java 不同。但是“资源”文件夹和相应的文本文件位置永远不会改变。如何在一种情况下找到文件,而在另一种情况下找不到文件?

【问题讨论】:

  • 另外三个问题:您使用的是 IDE(如 Eclipse)吗?您是否使用任何构建工具(如 Maven)?您的文本文件是打算由用户更新还是分发的一部分?
  • 您能否尝试确保您用于获取类加载器的类对象实际上是由 try { Class.forName(MainClass.class.getName()); } catch (final ClassNotFoundException ex) { } 加载的?
  • @Seelenvirtuose 两者都是:我正在使用 IntelliJ,并且该项目确实是使用 Maven 构建的。文本文件是静态的并且是分发的一部分。它们包含热图的 RGB 代码。
  • @muued 我将此代码添加到getLineFromLUT 的开头,执行时没有任何反应。所以我猜 MainClass 确实找到了。执行MainClass.javaLookUpClass.java
  • 由于文件位于资源目​​录中,我建议使用URL url = LookUpClass.class.getClass().getResource("resources/LookUpTables/LookUpTable1.txt");,只要项目不作为 jar 运行,我就可以使用它。

标签: java packages getresource directory-structure


【解决方案1】:

鉴于您提供的 MWE,我尝试重现您的问题,但没有成功。我在这里上传了我的项目,包括一个 pom.xml(你提到你使用了 maven):http://www.filedropper.com/stackoverflow 这是我的查找类的样子(还展示了如何使用 getResourceAsStream 方法):

public class LookUpClass {

    final static String tableName = "resources/LookUpTables/LookUpTable1.txt";

    public static String getLineFromLUT(final int line) {
        final URL url = LookUpClass.class.getResource(tableName);
        if (url.toString().startsWith("jar:")) {
            try (final URLClassLoader loader = URLClassLoader
                    .newInstance(new URL[] { url })) {
                return getLineFromLUT(
                        new InputStreamReader(
                                loader.getResourceAsStream(tableName)), line);
            } catch (final IOException e) {
                e.printStackTrace();
            }
        } else {
            return getLineFromLUT(
                    new InputStreamReader(
                            LookUpClass.class.getResourceAsStream(tableName)),
                    line);
        }
        return null;
    }

    public static String getLineFromLUT(final Reader reader, final int line) {
        try (final BufferedReader br = new BufferedReader(reader)) {
            for (int i = 0; i < line; ++i)
                br.readLine();
            return br.readLine();
        } catch (final IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}

【讨论】:

  • 感谢您花时间帮我解决问题!特别是如何使用 getResourceAsStream() 然后读取一行的部分非常有帮助。
【解决方案2】:

Maven 有一个standard directory layout。目录src/main/resources 用于此类应用程序资源。将您的文本文件放入其中。

您现在基本上有两个选择来准确放置文件:

  1. 资源文件属于一个类。

这方面的一个例子是一个表示 GUI 元素(面板)的类,它还需要显示一些图像。

在这种情况下,将资源文件放在与相应类相同的目录(包)中。例如。对于名为your.pkg.YourClass 的类,将资源文件放入目录your/pkg

src/main
 +-- java/
 |    +-- your/pkg/
 |    |    +-- YourClass.java
 +-- resources/
      +-- your/pkg/
           +-- resource-file.txt

您现在通过相应的类加载资源。在 your.pkg.YourClass 类中,您有以下代码 sn-p 用于加载:

String resource = "resource-file.txt"; // the "file name" without any package or directory
Class<?> clazz = this.getClass(); // or YourClass.class
URL resourceUrl = clazz.getResource(resource);
if (resourceUrl != null) {
    try (InputStream input = resourceUrl.openStream()) {
        // load the resource here from the input stream
    }
}

注意:你也可以通过类的类加载器来加载资源:

String resource = "your/pkg/resource-file.txt";
ClassLoader loader = this.getClass().getClassLoader(); // or YourClass.class.getClassLoader()
URL resourceUrl = loader.getResource(resource);
if (resourceUrl != null) {
    try (InputStream input = resourceUrl.openStream()) {
        // load the resource here from the input stream
    }
}

选择,你觉得更方便。

  1. 资源整体属于应用程序。

在这种情况下,只需将资源直接放入src/main/resources 目录或适当的子目录即可。让我们看一个带有您的查找文件的示例:

src/main/resources/
    +-- LookupTables/
         +-- LookUpTable1.txt

然后您必须通过类加载器加载资源,使用当前线程的上下文类加载器或应用程序类加载器(任何更合适的 - 如果感兴趣,请搜索有关此问题的文章)。我将向您展示两种方式:

String resource = "LookupTables/LookUpTable1.txt";
ClassLoader ctxLoader = Thread.currentThread().getContextClassLoader();
ClassLoader sysLoader = ClassLoader.getSystemClassLoader();
URL resourceUrl = ctxLoader.getResource(resource); // or sysLoader.getResource(resource)
if (resourceUrl != null) {
    try (InputStream input = resourceUrl.openStream()) {
        // load the resource here from the input stream
    }
}

作为第一个建议,使用当前线程的上下文类加载器。在独立应用程序中,这将是系统类加载器或将系统类加载器作为父级。 (这些类加载器之间的区别对于同时加载资源的库将变得很重要。)

您应该始终使用类加载器来加载资源。通过这种方式,您可以独立于位置进行加载(只需在启动应用程序时注意文件位于类路径中),并且您可以将整个应用程序打包成一个 JAR 文件,该文件仍然可以找到资源。

【讨论】:

  • 非常感谢您花时间解释一切并展示示例!我终于让它与线程的上下文加载器和系统类加载器一起工作。我还没有尝试你的第一个解决方案,但我计划接下来这样做,因为查找表实际上只属于/由这个类使用。 (抱歉回复晚了,由于复活节假期延长周末去了。)
  • 奇怪的是,在尝试方法 1 时,如果我实际上添加了完整的文件夹路径(即String resource = "your/pkg/resource-file.txt"),代码就可以工作,但如果我只使用文件名(@987654333),它就不起作用@)。我错过了什么吗?
  • @fukiburi 不,你没有错过什么。我做到了!我混淆了ClassLoader.getResourceClass.getResource。使用类加载器时,您还必须指定(类路径相对)路径。我会相应地编辑我的答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-21
  • 1970-01-01
  • 1970-01-01
  • 2018-07-27
相关资源
最近更新 更多