检索子包类的资源

时间:2015-04-02 13:04:20

标签: java packages getresource directory-structure

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

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

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

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

  • SRC
    • 的java
      • main.package.com
        • 子包
          • LookUpClass.java
          • PlotterClass.java
        • MainClass.java
    • 资源
      • LookUpTables
        • LookUpTable1.txt
        • LookUpTable2.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” “../子包/ LookUpTables / LookUpTable1.txt”
  • 自从使用Idea IntelliJ以来,我选中了“设置&gt;构建,执行,部署&gt; Compiter&gt;资源模式”,并在模式中添加了“* .txt”。什么都没有改变。
  • 如果添加Class c = LookUpClass.class.getClass();,则在调试模式c中为“class.java.lang.Class”。我期待像“main.package.com.subpackage.LookUpClass”这样的东西。
  • 在某些时候我尝试使用getResourceAsStream(),但我不明白如何获得我的(例如)第5行,所以我放弃了它。如果它解决了我的问题,我愿意读这个。

我不知道如何解决这个问题。而且我意识到,在这一点上,我只是在尝试一些事情,甚至不理解为什么它可以或不可行。 对我而言,似乎LookUpClass.java来自与MainClass.java不同的位置。但“资源”文件夹和相应的文本文件位置永远不会改变。如何在一个案例中找到该文件,而在另一个案例中却找不到?

2 个答案:

答案 0 :(得分:2)

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

您现在基本上有两个选项可以放置文件:

  1. 资源文件属于一个类。
  2. 这方面的一个例子是一个表示需要同时显示一些图像的GUI元素(面板)的类。

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

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

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

    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
        }
    }
    

    注意:您还可以通过类'class loader:

    加载资源
    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. 资源完全属于整个应用程序。
    2. 在这种情况下,只需将资源直接放入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 :(得分:0)

考虑到您提供的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;
    }
}