【问题标题】:Internal JAR uses files on the file system内部 JAR 使用文件系统上的文件
【发布时间】:2012-10-09 10:27:12
【问题描述】:

我有一个用例,我需要将这段特定的代码导出为 java 库(最终将是一个 JAR),但问题是它需要使用存储在文件上的物理文件中的一些信息系统。

我有两个问题:

1) 我应该将这些文件放在文件系统的什么位置(我能想到的一个选项是在包含库的 Java 模块的资源目录中:尽管资源目录也被编译到 jar 中,但有疑问吗? )

2) 当我从外部 Java 应用程序使用此库时,该库如何能够找到文件?他们还会在类路径中吗?

【问题讨论】:

  • 这是什么文件?配置文件?是否应该覆盖此文件?
  • 不清楚您是想用 JAR 移动文件(使用 getResourceAsStream)还是文件独立于 JAR(在用户可编辑的 .properties 中指定路径 /将其作为启动参数传递,由注销回答)
  • @Pierre-HenriToussaint - 是的,你可以把它当作一个配置文件,但我想在这里问的一点是:让我们把它限制在我有需要暴露的类的 jar 中各种应用。这些类需要访问这些配置文件(这些是固定的,独立于使用这些类的应用程序)。当这个 jar 被导入到应用程序中,并且应用程序尝试使用上述 jar 中的一个类来尝试从配置文件中检索一些信息时,一切都会正常工作而没有任何类路径问题吗?
  • 如果“配置文件”不是 java 类,也不是用于以某种自定义方式定义 java 依赖项 - 您不会遇到任何类路径问题。

标签: java file-io jar static-libraries


【解决方案1】:

您有两个选择,第一个是将文件放在包结构中,以便将它们打包到 jar 中。您可以从如下代码中获取它们:

getClass().getResourceAsStream("/path/to/your/resource/resource.ext");

如果你要从名为 A 的类的静态方法中调用它,那么你应该这样写:

A.class.getResourceAsStream("/path/to/your/resource/resource.ext");

路径的“/path”部分是最顶层的包,resource.ext是你的文件名。

另一种选择是将它们放在jar包之外,但是jar需要知道它们的位置:

  • 将其作为参数提供给程序 (java -jar program.jar system/path/to/file)
  • 使用路径硬编码读取文件的位置

我解开你的问题并回答的方式,与classpath无关:

CLASSPATH 变量是告诉应用程序(包括 JDK 工具)在哪里查找用户类的一种方式。 (属于 JRE、JDK 平台和扩展的类应通过其他方式定义,例如引导类路径或扩展目录。)

编辑: 但是您仍然可以将它放在那里并从这样的代码中获取它:

System.getProperty("java.class.path");

然而,它需要一些逻辑来解析它。

【讨论】:

    【解决方案2】:

    您可以在属性文件或类似的技术中传递文件的位置。

    【讨论】:

      【解决方案3】:

      我应该把这些文件放在文件系统的什么地方

      这由您决定,但最好将其设置为可配置。尝试适应主机操作系统/发行版的约定也是一个好主意,尽管这些会有所不同......并且取决于您的应用程序的性质。

      当我从外部 Java 应用程序使用此库时,该库如何能够找到文件?

      您通常会使用配置属性或初始化参数来保存/传递位置。如果您正在编写应用程序而不是库,则可以使用Java Preferences APIs,尽管这对于库来说可能是一个糟糕的选择。

      它们还会在类路径中吗?

      仅当您将位置放在类路径上...这将使配置更加棘手。鉴于这些文件需要存储在文件系统中,我建议使用FileInputStream 或类似的。

      【讨论】:

      • @StephenC-我已经有了这些文件。我想知道一种将这些文件打包到 jar 本身中的方法,以便使用 jar 的独立应用程序不必关心这些文件。为了让问题更清楚一点,我想确认当这些文件是 jar 的一部分并且被同一个 jar 中的类引用,并且这个 jar 被其他几个应用程序使用时,引用是否相同因为我们会访问模块中的普通资源文件?我的意思是,从应用程序的角度来看,类路径是否也包含这些文件?
      【解决方案4】:

      使用 Eclipse,我总是创建一个包“资源”,将 jar 需要的文件放在其中。我通过

      访问文件(几乎可以从任何地方)
      this.getClass().getClassLoader().getResources("/resources/file.ext");
      

      使用 export->runnable jar,所有这些文件都包含在 .jar 中。我不确定这是不是正确的做法。另外,我不是 100% 确定资源前的“/”,也许应该省略它。

      【讨论】:

      • OP 提到 Eclipse 了吗?他实际上是在谈论将独立于 Eclipse 安装的应用程序/库。
      【解决方案5】:

      我在另一个问题中找到了相关答案:How to load a folder from a .jar?

      我能够使用以下代码成功检索文件:

         /**
         * List directory contents for a resource folder. Not recursive.
         * This is basically a brute-force implementation.
         * Works for regular files and also JARs.
         * 
         * @author Greg Briggs
         * @param clazz Any java class that lives in the same place as the resources you want.
         * @param path Should end with "/", but not start with one.
         * @return Just the name of each member item, not the full paths.
         * @throws URISyntaxException 
         * @throws IOException 
         */
        String[] getResourceListing(Class clazz, String path) throws URISyntaxException, IOException {
            URL dirURL = clazz.getClassLoader().getResource(path);
            if (dirURL != null && dirURL.getProtocol().equals("file")) {
              /* A file path: easy enough */
              return new File(dirURL.toURI()).list();
            } 
      
            if (dirURL == null) {
              /* 
               * In case of a jar file, we can't actually find a directory.
               * Have to assume the same jar as clazz.
               */
              String me = clazz.getName().replace(".", "/")+".class";
              dirURL = clazz.getClassLoader().getResource(me);
            }
      
            if (dirURL.getProtocol().equals("jar")) {
              /* A JAR path */
              String jarPath = dirURL.getPath().substring(5, dirURL.getPath().indexOf("!")); //strip out only the JAR file
              JarFile jar = new JarFile(URLDecoder.decode(jarPath, "UTF-8"));
              Enumeration<JarEntry> entries = jar.entries(); //gives ALL entries in jar
              Set<String> result = new HashSet<String>(); //avoid duplicates in case it is a subdirectory
              while(entries.hasMoreElements()) {
                String name = entries.nextElement().getName();
                if (name.startsWith(path)) { //filter according to the path
                  String entry = name.substring(path.length());
                  int checkSubdir = entry.indexOf("/");
                  if (checkSubdir >= 0) {
                    // if it is a subdirectory, we just return the directory name
                    entry = entry.substring(0, checkSubdir);
                  }
                  result.add(entry);
                }
              }
              return result.toArray(new String[result.size()]);
            } 
      
            throw new UnsupportedOperationException("Cannot list files for URL "+dirURL);
        }
      

      【讨论】:

        猜你喜欢
        • 2017-06-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-06-14
        • 1970-01-01
        • 2014-07-12
        • 2015-09-23
        • 2017-09-22
        相关资源
        最近更新 更多