如何从JavaJAR文件中读取资源文件?

如何从JavaJAR文件中读取资源文件?,java,jar,resources,Java,Jar,Resources,我试图从一个单独的jar(作为桌面应用程序运行)中访问jar文件中的XML文件。我可以获取所需文件的URL,但当我将其作为字符串传递给文件读取器时,我会得到一个FileNotFoundException,其中说“文件名、目录名或卷标语法不正确。” 作为参考,我可以从同一个jar读取图像资源,并将URL传递给ImageIcon构造函数。这似乎表明我用来获取URL的方法是正确的 URL url = getClass().getResource("/xxx/xxx/xxx/services.xml")

我试图从一个单独的jar(作为桌面应用程序运行)中访问jar文件中的XML文件。我可以获取所需文件的URL,但当我将其作为字符串传递给文件读取器时,我会得到一个FileNotFoundException,其中说“文件名、目录名或卷标语法不正确。”

作为参考,我可以从同一个jar读取图像资源,并将URL传递给ImageIcon构造函数。这似乎表明我用来获取URL的方法是正确的

URL url = getClass().getResource("/xxx/xxx/xxx/services.xml");
ServicesLoader jsl = new ServicesLoader( url.toString() );
在我的ServicesLoader类中

XMLReader xr = XMLReaderFactory.createXMLReader();
xr.setContentHandler( this );
xr.setErrorHandler( this );
xr.parse( new InputSource( new FileReader( filename )));

使用此技术读取XML文件有什么问题?

您不能说这是桌面应用还是web应用。如果是桌面,我会使用相应类加载器中的
getResourceAsStream()
方法;如果是web应用,我会使用上下文方法。

在您的技术之外,为什么不使用标准Java来获取您想要的引用?从那以后,您的大多数问题都应该消失。

看起来您想要使用
java.lang.Class.getResourceAsStream(String)
,请参阅


看起来您正在使用
URL.toString
结果作为
FileReader
构造函数的参数
URL.toString
有点不完整,通常应该使用
URL.toURI().toString()
。在任何情况下,字符串都不是文件路径

相反,您应该:

  • URL
    传递给
    ServicesLoader
    ,并让它调用
    openStream
    或类似功能
  • 使用
    Class.getResourceAsStream
    并将流传递过来,可能在
    InputSource
    中。(记住检查空值,因为API有点凌乱。)

    • 问题是我调用XMLReader的解析方法时走得太远了。parse方法接受InputSource,因此甚至没有理由使用FileReader。将上面代码的最后一行更改为

      xr.parse( new InputSource( filename ));
      

      工作很好。

      < P>如果你广泛地使用资源,你可以考虑使用。

      还支持: *本地文件 *FTP,SFTP *HTTP和HTTPS *临时文件(支持普通FS) *Zip、Jar和Tar(未压缩、tgz或tbz2) *gzip和bzip2 *资源 *ram-“ramdrive” *默剧


      还有-但没有太多的文档。

      我有两个用于读取数据的CSV文件。java程序被导出为可运行的jar文件。当您导出它时,您会发现它没有导出您的资源

      我在eclipse项目下添加了一个名为data的文件夹。在该文件夹中我存储了我的csv文件

      当我需要引用这些文件时,我会这样做

      private static final String ZIP_FILE_LOCATION_PRIMARY = "free-zipcode-database-Primary.csv";
      private static final String ZIP_FILE_LOCATION = "free-zipcode-database.csv";
      
      private static String getFileLocation(){
          String loc = new File("").getAbsolutePath() + File.separatorChar +
              "data" + File.separatorChar;
          if (usePrimaryZipCodesOnly()){              
              loc = loc.concat(ZIP_FILE_LOCATION_PRIMARY);
          } else {
              loc = loc.concat(ZIP_FILE_LOCATION);
          }
          return loc;
      }
      

      然后,当您将jar放在一个可以通过命令行运行的位置时,请确保将包含资源的数据文件夹添加到与jar文件相同的位置。

      我想指出的一个问题是,如果相同的资源位于多个jar文件中会发生什么。 假设您想读取/org/node/foo.txt,但不是从一个文件中读取,而是从每个jar文件中读取

      我以前曾多次遇到过同样的问题。 我希望在JDK7中有人会编写一个类路径文件系统,但遗憾的是现在还没有

      Spring有一个资源类,它允许您很好地加载类路径资源

      我编写了一个小原型来解决从多个jar文件中读取资源的问题。这个原型并不能处理所有的边缘情况,但它可以处理在jar文件中的目录中查找资源的问题

      我使用堆栈溢出已经有一段时间了。这是我记得回答问题的第二个答案,所以如果我回答的时间太长,请原谅我(这是我的天性)

      这是一个原型资源读取器。该原型缺少健壮的错误检查

      我已经设置了两个原型jar文件

       &lt;pre>
               &lt;dependency>
                    &lt;groupId>invoke&lt;/groupId>
                    &lt;artifactId>invoke&lt;/artifactId>
                    &lt;version>1.0-SNAPSHOT&lt;/version>
                &lt;/dependency>
      
                &lt;dependency>
                     &lt;groupId>node&lt;/groupId>
                     &lt;artifactId>node&lt;/artifactId>
                     &lt;version>1.0-SNAPSHOT&lt;/version>
                &lt;/dependency>
      
      jar文件在/org/node/resource.txt下都有一个名为resource.txt的文件

      这只是类路径处理程序的原型:// 我的本地资源中也有一个resource.foo.txt用于此项目

      它会把它们全部收集起来并打印出来

      
      
          package com.foo;
      
          import java.io.File;
          import java.io.FileReader;
          import java.io.InputStreamReader;
          import java.io.Reader;
          import java.net.URI;
          import java.net.URL;
          import java.util.Enumeration;
          import java.util.zip.ZipEntry;
          import java.util.zip.ZipFile;
      
          /**
          * Prototype resource reader.
          * This prototype is devoid of error checking.
          *
          *
          * I have two prototype jar files that I have setup.
          * <pre>
          *             <dependency>
          *                  <groupId>invoke</groupId>
          *                  <artifactId>invoke</artifactId>
          *                  <version>1.0-SNAPSHOT</version>
          *              </dependency>
          *
          *              <dependency>
          *                   <groupId>node</groupId>
          *                   <artifactId>node</artifactId>
          *                   <version>1.0-SNAPSHOT</version>
          *              </dependency>
          * </pre>
          * The jar files each have a file under /org/node/ called resource.txt.
          * <br />
          * This is just a prototype of what a handler would look like with classpath://
          * I also have a resource.foo.txt in my local resources for this project.
          * <br />
          */
          public class ClasspathReader {
      
              public static void main(String[] args) throws Exception {
      
                  /* This project includes two jar files that each have a resource located
                     in /org/node/ called resource.txt.
                   */
      
      
                  /* 
                    Name space is just a device I am using to see if a file in a dir
                    starts with a name space. Think of namespace like a file extension 
                    but it is the start of the file not the end.
                  */
                  String namespace = "resource";
      
                  //someResource is classpath.
                  String someResource = args.length > 0 ? args[0] :
                          //"classpath:///org/node/resource.txt";   It works with files
                          "classpath:///org/node/";                 //It also works with directories
      
                  URI someResourceURI = URI.create(someResource);
      
                  System.out.println("URI of resource = " + someResourceURI);
      
                  someResource = someResourceURI.getPath();
      
                  System.out.println("PATH of resource =" + someResource);
      
                  boolean isDir = !someResource.endsWith(".txt");
      
      
                  /** Classpath resource can never really start with a starting slash.
                   * Logically they do, but in reality you have to strip it.
                   * This is a known behavior of classpath resources.
                   * It works with a slash unless the resource is in a jar file.
                   * Bottom line, by stripping it, it always works.
                   */
                  if (someResource.startsWith("/")) {
                      someResource = someResource.substring(1);
                  }
      
                    /* Use the ClassLoader to lookup all resources that have this name.
                       Look for all resources that match the location we are looking for. */
                  Enumeration resources = null;
      
                  /* Check the context classloader first. Always use this if available. */
                  try {
                      resources = 
                          Thread.currentThread().getContextClassLoader().getResources(someResource);
                  } catch (Exception ex) {
                      ex.printStackTrace();
                  }
      
                  if (resources == null || !resources.hasMoreElements()) {
                      resources = ClasspathReader.class.getClassLoader().getResources(someResource);
                  }
      
                  //Now iterate over the URLs of the resources from the classpath
                  while (resources.hasMoreElements()) {
                      URL resource = resources.nextElement();
      
      
                      /* if the resource is a file, it just means that we can use normal mechanism
                          to scan the directory.
                      */
                      if (resource.getProtocol().equals("file")) {
                          //if it is a file then we can handle it the normal way.
                          handleFile(resource, namespace);
                          continue;
                      }
      
                      System.out.println("Resource " + resource);
      
                     /*
      
                       Split up the string that looks like this:
                       jar:file:/Users/rick/.m2/repository/invoke/invoke/1.0-SNAPSHOT/invoke-1.0-SNAPSHOT.jar!/org/node/
                       into
                          this /Users/rick/.m2/repository/invoke/invoke/1.0-SNAPSHOT/invoke-1.0-SNAPSHOT.jar
                       and this
                           /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);
      
      
                      /* Open up the zip file. */
                      ZipFile zipFile = new ZipFile(zipFileName);
      
      
                      /*  Iterate through the entries.  */
                      Enumeration 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 it does not start with our someResource String
                             then it is not our resource so continue.
                          */
                          if (!entryName.startsWith(someResource)) {
                              continue;
                          }
      
      
                          /* the fileName part from the entry name.
                           * where /foo/bar/foo/bee/bar.txt, bar.txt is the file
                           */
                          String fileName = entryName.substring(entryName.lastIndexOf("/") + 1);
                          System.out.printf("fileName %s \n", fileName);
      
                          /* See if the file starts with our namespace and ends with our extension.        
                           */
                          if (fileName.startsWith(namespace) && fileName.endsWith(".txt")) {
      
      
                              /* If you found the file, print out 
                                 the contents fo the file to 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();
                              }
                          }
      
                          //use the entry to see if it's the file '1.txt'
                          //Read from the byte using file.getInputStream(entry)
                      }
      
                  }
      
      
              }
      
              /**
               * The file was on the file system not a zip file,
               * this is here for completeness for this example.
               * otherwise.
               *
               * @param resource
               * @param namespace
               * @throws Exception
               */
              private static void handleFile(URL resource, String namespace) throws Exception {
                  System.out.println("Handle this resource as a file " + resource);
                  URI uri = resource.toURI();
                  File file = new File(uri.getPath());
      
      
                  if (file.isDirectory()) {
                      for (File childFile : file.listFiles()) {
                          if (childFile.isDirectory()) {
                              continue;
                          }
                          String fileName = childFile.getName();
                          if (fileName.startsWith(namespace) && fileName.endsWith("txt")) {
      
                              try (FileReader reader = new FileReader(childFile)) {
                                  StringBuilder builder = new StringBuilder();
                                  int ch = 0;
                                  while ((ch = reader.read()) != -1) {
                                      builder.append((char) ch);
      
                                  }
                                  System.out.printf("fileName = %s\n\n####\n contents of file %s\n###\n", childFile, builder);
                              } catch (Exception ex) {
                                  ex.printStackTrace();
                              }
      
                          }
      
                      }
                  } else {
                      String fileName = file.getName();
                      if (fileName.startsWith(namespace) && fileName.endsWith("txt")) {
      
                          try (FileReader reader = new FileReader(file)) {
                              StringBuilder builder = new StringBuilder();
                              int ch = 0;
                              while ((ch = reader.read()) != -1) {
                                  builder.append((char) ch);
      
                              }
                              System.out.printf("fileName = %s\n\n####\n contents of file %s\n###\n", fileName, builder);
                          } catch (Exception ex) {
                              ex.printStackTrace();
                          }
      
                      }
      
                  }
              }
      
          }
      
      
         
      
      包com.foo;
      导入java.io.File;
      导入java.io.FileReader;
      导入java.io.InputStreamReader;
      导入java.io.Reader;
      导入java.net.URI;
      导入java.net.URL;
      导入java.util.Enumeration;
      导入java.util.zip.ZipEntry;
      导入java.util.zip.ZipFile;
      /**
      *原型资源阅读器。
      *这个原型没有错误检查。
      *
      *
      *我已经设置了两个原型jar文件。
      * 
      *             
      *援引
      
      *

      下面是一个示例代码,介绍如何正确读取jar文件中的文件(在本例中,是当前正在执行的jar文件)

      只要用jar文件的路径更改可执行文件,如果它不是当前正在运行的文件

      然后将文件路径更改为要在jar文件中使用的文件的路径

      someJar.jar\img\test.gif

      。将文件路径设置为“img\test.gif”


      @JVerstry您的回答对我很有帮助,遗憾的是,它被完全低估了。您可以发布工作代码吗?请确保在使用文件API时,使用unix文件路径分隔符。
      File executable = new File(BrowserViewControl.class.getProtectionDomain().getCodeSource().getLocation().toURI());
      JarFile jar = new JarFile(executable);
      InputStream fileInputStreamReader = jar.getInputStream(jar.getJarEntry(filePath));
      byte[] bytes = new byte[fileInputStreamReader.available()];
      
      int sizeOrig = fileInputStreamReader.available();
      int size = fileInputStreamReader.available();
      int offset = 0;
      while (size != 0){
          fileInputStreamReader.read(bytes, offset, size);
          offset = sizeOrig - fileInputStreamReader.available();
          size = fileInputStreamReader.available();
      }