2008-12-31 150 views
45

我试图从作为桌面应用程序运行的单独的jar中访问jar文件中的XML文件。我可以得到我需要的文件的URL,但是当我将它传递给FileReader(作为字符串)时,我得到一个FileNotFoundException,指出“文件名,目录名或卷标语法不正确。”如何从Java 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文件?

回答

4

问题是我在调用XMLReader的分析方法时太过分了。解析方法接受一个I​​nputSource,所以没有理由甚至使用FileReader。将上面代码的最后一行更改为

xr.parse(new InputSource(filename)); 

工作得很好。

5

你不说,如果这是一个桌面或Web应用程序。如果是桌面,我会使用适当的ClassLoader的getResourceAsStream()方法,如果是Web应用程序,则使用Context。

+0

这是一个桌面应用程序。我将编辑该问题。 – 2008-12-31 15:52:32

0

你的技术之外,为什么不使用标准的Java JarFile class得到你想要的参考?从那里你的大部分问题应该消失。

4

看起来好像你正在使用URL.toString结果作为参数传递给FileReader构造。 URL.toString是有点破,而应该通常使用url.toURI().toString()。在任何情况下,该字符串都不是文件路径。

相反,你应该:

  • 传递URLServicesLoader,让它叫openStream或相似。
  • 使用Class.getResourceAsStream,只是通过流过,可能是一个InputSource内。 (请记住检查空值,因为API是有点混乱。)
0

如果使用大量的资源,你可以考虑使用 Commons VFS

还支持: *本地文件 * FTP,SFTP * HTTP和HTTPS *临时文件“正常FS支持) *邮编,罐和焦油(未压缩的,TGZ或tbz2) * gzip和bzip2的 *资源 * RAM - “RAMDRIVE” *哑剧

还有JBoss VFS - 。但它没有太多记载

0

我有,我用2个CSV文件读取数据的Java程序是导出为AR不可用的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文件相同的位置。

2

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

我以前遇到过几次同样的问题。 我希望在JDK 7中有人会写一个类路径文件系统,但还没有。

Spring拥有Resource类,它允许您很好地加载classpath资源。

我写了一个小原型来解决这个从多个jar文件中读取资源的问题。原型不处理每个边界情况,但它确实处理在jar文件中的目录中查找资源。

我已经在一段时间内使用了Stack Overflow。这是我记得回答一个问题的第二个答案,所以如果我的时间太长(这是我的本性),请原谅我。

这是一个原型资源阅读器。该原型缺乏可靠的错误检查。

我有两个原型jar文件,我已经设置。

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

这些jar文件每个都在/ org/node /下有一个名为resource.txt的文件。

这只是一个类处理程序看起来像classpath的原型:// 我在这个项目的本地资源中也有一个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(); 
        } 

       } 

      } 
     } 

    } 


 

You can see a fuller example here with the sample output.