2008-12-31 32 views
45

Tôi đang cố truy cập tệp XML trong tệp jar, từ một bình riêng biệt đang chạy dưới dạng ứng dụng dành cho máy tính để bàn. Tôi có thể lấy URL cho tập tin tôi cần, nhưng khi tôi chuyển nó đến một FileReader (như một String), tôi nhận được một FileNotFoundException nói "Tên tập tin, tên thư mục, hoặc cú pháp nhãn khối lượng là không chính xác."Làm cách nào để đọc tệp tài nguyên từ tệp jar Java?

Là một điểm tham chiếu, tôi không gặp khó khăn khi đọc tài nguyên hình ảnh từ cùng một lọ, chuyển URL tới một hàm tạo ImageIcon. Điều này dường như chỉ ra rằng phương pháp tôi đang sử dụng để lấy URL là chính xác.

URL url = getClass().getResource("/xxx/xxx/xxx/services.xml"); 
ServicesLoader jsl = new ServicesLoader(url.toString()); 

Bên trong lớp ServicesLoader Tôi có

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

Có gì sai với việc sử dụng kỹ thuật này để đọc các tập tin XML?

Trả lời

4

Vấn đề là tôi đã đi quá xa để gọi phương thức phân tích cú pháp của XMLReader. Phương thức phân tích cú pháp chấp nhận một InputSource, vì vậy không có lý do gì để thậm chí sử dụng một FileReader. Thay đổi dòng cuối cùng của mã ở trên thành

xr.parse(new InputSource(filename)); 

chỉ hoạt động tốt.

5

Bạn không nói nếu đây là ứng dụng dành cho máy tính để bàn hoặc web. Tôi sẽ sử dụng phương thức getResourceAsStream() từ một Trình nạp lớp thích hợp nếu đó là máy tính để bàn hoặc Bối cảnh nếu đó là một ứng dụng web.

+0

Đó là một ứng dụng máy tính để bàn. Tôi sẽ chỉnh sửa câu hỏi. –

0

Ngoài kỹ thuật của bạn, tại sao không sử dụng tiêu chuẩn Java JarFile class để nhận các tham chiếu bạn muốn? Từ đó hầu hết các vấn đề của bạn sẽ biến mất.

4

Có vẻ như nếu bạn đang sử dụng URL.toString kết quả như là đối số cho các nhà xây dựng FileReader. URL.toString bị hỏng một chút và thay vào đó, bạn thường sử dụng url.toURI().toString(). Trong mọi trường hợp, chuỗi không phải là đường dẫn tệp.

Thay vào đó, bạn nên một trong hai:

  • Vượt qua URL để ServicesLoader và để cho nó gọi openStream hoặc tương đương.
  • Sử dụng Class.getResourceAsStream và chỉ chuyển luồng qua, có thể bên trong một InputSource. (Hãy nhớ kiểm tra các giá trị rỗng vì API hơi lộn xộn.)
0

Nếu bạn sử dụng tài nguyên rộng rãi, bạn có thể xem xét sử dụng Commons VFS.

Cũng hỗ trợ: * tập tin cục bộ * FTP, SFTP * HTTP và HTTPS * tập tin tạm thời "FS bình thường hậu thuẫn) * Zip, Jar và Tar (không nén, tgz hoặc tbz2) * gzip và bzip2 * nguồn * ram - "ramdrive" * kịch câm

Ngoài ra còn có JBoss VFS -.. nhưng nó không có nhiều tài liệu

0

tôi có 2 file CSV mà tôi sử dụng để đọc dữ liệu chương trình java được xuất khẩu như ar tệp jar không cần thiết. Khi bạn xuất nó, bạn sẽ tìm ra nó không xuất các tài nguyên của bạn với nó.

Tôi đã thêm một thư mục trong dự án được gọi là dữ liệu trong nhật thực. Trong thư mục đó, tôi đã lưu trữ các tệp csv của mình.

Khi tôi cần phải tham khảo các tập tin tôi làm điều đó như thế này ...

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

Sau đó, khi bạn đặt jar ở một vị trí để có thể chạy qua dòng lệnh, hãy chắc chắn rằng bạn thêm dữ liệu thư mục với các tài nguyên vào cùng một vị trí với tệp jar.

2

Tôi muốn chỉ ra rằng một vấn đề là gì nếu cùng một tài nguyên nằm trong nhiều tệp jar. Giả sử bạn muốn đọc /org/node/foo.txt, nhưng không phải từ một tệp, nhưng từ mỗi tệp jar.

Tôi đã gặp phải vấn đề tương tự này vài lần trước đây. Tôi đã hy vọng trong JDK 7 rằng ai đó sẽ viết một hệ thống tập tin classpath, nhưng vẫn chưa.

Mùa xuân có lớp Tài nguyên cho phép bạn tải các tài nguyên lớp học khá độc đáo.

Tôi đã viết một mẫu thử nghiệm nhỏ để giải quyết vấn đề này rất lớn về đọc tài nguyên tạo thành nhiều tệp jar. Nguyên mẫu không xử lý mọi trường hợp cạnh, nhưng nó xử lý tìm kiếm các tài nguyên trong các thư mục nằm trong các tệp jar.

Tôi đã sử dụng Stack Overflow cho đôi khi. Đây là câu trả lời thứ hai mà tôi nhớ trả lời một câu hỏi để tha thứ cho tôi nếu tôi đi quá lâu (đó là bản chất của tôi).

Đây là trình đọc tài nguyên nguyên mẫu. Nguyên mẫu là không có kiểm tra lỗi mạnh mẽ.

Tôi có hai tệp mẫu thử nghiệm mà tôi đã thiết lập.

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

Mỗi tệp jar có tệp dưới/org/node/named resource.txt.

Đây chỉ là mẫu thử nghiệm của trình xử lý giống như đường dẫn lớp: // Tôi cũng có resource.foo.txt trong tài nguyên cục bộ của mình cho dự án này.

Nó chọn tất cả chúng và in chúng ra.

 
    

    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.

Các vấn đề liên quan