【问题标题】:Access external .jar resources from java从 java 访问外部 .jar 资源
【发布时间】:2012-04-27 22:10:04
【问题描述】:

我正在开发一个多模块 Java 项目,它由 Eclipse 中相互依赖的多个项目组成,现在添加 GUI 主题支持我创建了一个不包含任何代码的 Java 项目,只有图标和图片GUI 需要,我将其设为 Java 项目,因此 Maven 会将其构建为 .jar。现在有一个主应用程序将多个项目加载到一个主 GUI 中,该 GUI 目前从实际模块中的资源中获取其图标。我要做的就是从包含在主应用程序.jar 的类路径中的外部虚拟.jar 加载所有这些资源。到目前为止,我发现的每种方法似乎都不起作用。 .jar 不包含实际的 java .classes,因此没有要引用的 ClassLoader。有没有其他方法可以在不提取.jar的情况下加载图片?

【问题讨论】:

  • 这个问题看起来像是stackoverflow.com/questions/403256/…的副本
  • “他的 .jar 不包含实际的 java .classes,因此没有可引用的 ClassLoader。” 如果 Jar 在应用程序的运行时类路径上.,资源应该使用 getResource() (在上下文类加载器中)和“从根路径”找到,例如/path/to/the.png
  • 这实际上是我的一个简单错误(我使用了不存在的路径)。显然,无论从哪里调用 getResource(),Java 从类路径中获取资源都没有问题。有人向我解释说,Java 基本上是在运行时从所有使用过的 jar 中构建一个文件系统,因此访问非常简单且全局可用。谢谢大家的建议!

标签: java user-interface resources jar load


【解决方案1】:

你可以尝试的三件事:

  1. 在资源 jar 中创建一个虚拟类,您可以使用它来获取 ClassLoader 引用。
  2. 如果您知道 jar 文件所在的位置,请使用 URLClassLoader。
  3. 您始终可以通过扩展 java.lang.ClassLoader 创建自己的 ClassLoader。

【讨论】:

    【解决方案2】:

    将外部 jar 文件视为 zip 存档并读取图像/资源,如下所示:

    public ZipStuff(String filename) throws IOException
    {
        ZipFile zf = new ZipFile(filename);
        Enumeration<? extends ZipEntry> entries = zf.entries();
    
        while (entries.hasMoreElements()) {
          ZipEntry ze = entries.nextElement();
          ImageIcon ii = new ImageIcon(ImageIO.read(zf.getInputStream(ze)));
          JLabel l = new JLabel(ii);
          getContentPane().add(l);
        }
        setVisible(true);
        pack();
    }
    

    在这种情况下,我有一个带有单个图像的 jar 文件,我将其加载到 JLabel 中。 ZipStuff 类是一个 JFrame。

    【讨论】:

      【解决方案3】:

      您可以获得所有资源(classpath 上的所有 jar 文件即使没有类也应该可以工作):

          Enumeration<URL> resources = null;
          try {
              resources = Thread.currentThread().getContextClassLoader().getResources(someResource);
          } catch (Exception ex) {
               //no op      
          }
          if (resources == null || !resources.hasMoreElements()) {
              resources = ClasspathReader.class.getClassLoader().getResources(someResource);
          }
      

      然后检查当前资源是否为文件。可以直接作为文件处理的文件。 但是你的问题是关于 jar 文件的,所以我不会去那里。

          while (resources.hasMoreElements()) {
              URL resource = resources.nextElement();
      
      
              if (resource.getProtocol().equals("file")) {
                  //if it is a file then we can handle it the normal way.
                  handleFile(resource, namespace);
                  continue;
              }
      

      此时您应该只有 jar:file 资源,所以... 拆分如下所示的字符串:

      jar:file:/Users/rick/.m2/repository/invoke/invoke/1.0-SNAPSHOT/invoke-1.0-SNAPSHOT.jar!/org/node/

      进入这个

      /Users/rick/.m2/repository/invoke/invoke/1.0-SNAPSHOT/invoke-1.0-SNAPSHOT.jar

      还有这个

      /org/node/

      这是执行上述操作的代码,无需进行烦人的错误检查。 :)

              String[] split = resource.toString().split(":");
              String[] split2 = split[2].split("!");
              String zipFileName = split2[0];
              String sresource = split2[1];
      
              System.out.printf("After split zip file name = %s," +
                      " \nresource in zip %s \n", zipFileName, sresource);
      

      现在我们有了 zip 文件名,所以我们可以阅读它:

              ZipFile zipFile = new ZipFile(zipFileName);
      

      现在我们可以遍历它的条目:

              Enumeration<? extends ZipEntry> entries = zipFile.entries();
      
              while (entries.hasMoreElements()) {
                  ZipEntry entry = entries.nextElement();
      
                  /* If it is a directory, then skip it. */
                  if (entry.isDirectory()) {
                      continue;
                  }
      
                  String entryName = entry.getName();
                  System.out.printf("zip entry name %s \n", entryName);
      

      看看它是否以我们正在寻找的资源开头。

                  if (!entryName.startsWith(someResource)) {
                      continue;
                  }
      

      我之前做了两个技巧来查看它是否是一个目录

          boolean isDir = !someResource.endsWith(".txt");
      

      这仅有效,因为我正在寻找以 .txt 结尾的资源,并且我假设如果它不以 .txt 结尾,那么它是一个目录 /foo/dir 和 /foo/dir/ 都可以。

      另一个技巧是这样的:

          if (someResource.startsWith("/")) {
              someResource = someResource.substring(1);
          }
      

      类路径资源永远不能真正以斜杠开头。逻辑上他们这样做,但实际上你必须剥离它。这是类路径资源的已知行为。除非资源在 jar 文件中,否则它与斜线一起使用。最重要的是,通过剥离它,它总是有效的。

      我们需要得到该死的东西的实际文件名。条目名称中的文件名部分。 其中/foo/bar/foo/bee/bar.txt,我们想要'bar.txt',它是文件名。回到我们的 while 循环内部。

              while (entries.hasMoreElements()) {
                  ZipEntry entry = entries.nextElement();
                  ...
                  String entryName = entry.getName(); //entry is zipEntry
                  String fileName = entryName.substring(entryName.lastIndexOf("/") + 1);
      
      
                  /** See if the file starts with our namespace and ends with our extension. */
                  if (fileName.startsWith(namespace) && fileName.endsWith(".txt")) {
      

      接下来我们查看这些条目是否符合我们的条件,如果符合,则将文件内容读取到 System.out。

                try (Reader reader = new InputStreamReader(zipFile.getInputStream(entry))) {
                          StringBuilder builder = new StringBuilder();
                          int ch = 0;
                          while ((ch = reader.read()) != -1) {
                              builder.append((char) ch);
      
                          }
                          System.out.printf("zip fileName = %s\n\n####\n contents of file %s\n###\n", 
                          entryName, builder);
                      } catch (Exception ex) {
                          ex.printStackTrace();//it is an example/proto :)
                      }
                  }
      

      您可以在此处查看完整示例:Sleepless in Pleasanton

      【讨论】:

        猜你喜欢
        • 2016-12-29
        • 2015-09-06
        • 1970-01-01
        • 2013-01-02
        • 1970-01-01
        • 2016-05-13
        • 2012-05-11
        • 1970-01-01
        相关资源
        最近更新 更多