2008-12-31 129 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.