In-depth jar package: read resource files from jar package

We often read some resource files (such as pictures, music, text, etc.) in the code. These simple processes are of course no problem when run alone. However, if we package the code into a jar package, even if we package the resource files together, these things will not be found. Take a look at the code below:

Java code 
  1. //Source code 1:  
  2. package edu.hxraid;  
  3. import  java.io. *;  
  4. publicclass Resource {   
  5.     publicvoid getResource() throws IOException{    
  6.         File file=new File("bin/resource/res.txt");  
  7.         BufferedReader br=new BufferedReader(new FileReader(file));  
  8.         String s="";  
  9.         while((s=br.readLine())!=null)  
  10.             System.out.println(s);  
  11.     }  
  12. }     

        This code is written in the java Project established by Eclipse, and its directory is: (where the resource file res.txt is placed in the bin directory to make it into a jar package)
      1. src/
              src/edu/hxraid/Resource.java
      2. bin/
              bin/resource/res.txt
              bin/edu/hxraid/Resource.class

      Obviously running source code 1 is able to find the resource file res.txt. But when we put the whole project into a jar package (ResourceJar.jar), the directory in this jar package is:
              edu/hxraid/Resource.class
              resource/res.txt

         At this time, the Resource.class bytecode in the jar package: ldc <String "bin/resource/res.txt"> [20] will not be able to locate the res.txt location in the jar package. Even if the bin/ directory is removed: ldc <String "resource/res.txt"> [20] still cannot locate res.txt in the jar package.

      This is mainly because the jar package is a separate file rather than a folder, and it is absolutely impossible to locate res through a file URL of the form "file:/e:/.../ ResourceJar.jar /resource /res.txt" .TXT. So even if it is a relative path, it cannot locate the txt file in the jar file (the reader may be a little puzzled by the explanation of this reason, and we will use the result of running a piece of code to further elaborate).

          Then put the resources into the jar package, no matter what path ResourceJar.jar is in the system, the bytecode program in the jar package can find the resources in the package. Could this be a fantasy?

 

      Of course not, we can do this with a ClassLoader:

         (1)  ClassLoader is an abstract class of class loader. It can dynamically obtain running information of loaded classes at runtime.  It can be said that when we call the Resource class in ResourceJar.jar, the JVM loads the Resource class and records the Resource runtime information (including the path information of the jar package where the Resource is located). The methods in the ClassLoader class can help us obtain these information dynamically:
          ● public URL getResource(String name) 
            to find a resource with a given name. A resource is some data (image, sound, text, etc.) that can be accessed by class code in a codebase-independent manner. and returns the URL object of the resource.
          ● public InputStream getResourceAsStream(String name); 
             Returns the input stream for reading the specified resource. This method is very important, you can directly get the contents of the files in the jar package.

          

          (2) ClassLoader是abstract的,不可能实例化对象,更加不可能通过ClassLoader调用上面两个方法。所以我们真正写代码的时候,是通过Class类中的getResource()和getResourceAsStream()方法,这两个方法会委托ClassLoader中的getResource()和getResourceAsStream()方法 。好了,现在我们重新写一段Resource代码,来看看上面那段费解的话是什么意思了:

Java代码 
  1. //源代码2:  
  2. package edu.hxraid;  
  3. import java.io.*;  
  4. import java.net.URL;  
  5. public class Resource {  
  6.     public  void getResource() throws IOException{    
  7.               //查找指定资源的URL,其中res.txt仍然开始的bin目录下   
  8.         URL fileURL=this.getClass().getResource("/resource/res.txt");   
  9.         System.out.println(fileURL.getFile());  
  10.     }  
  11.     public static void main(String[] args) throws IOException {  
  12.         Resource res=new Resource();  
  13.         res.getResource();  
  14.     }  
  15. }  

        运行这段源代码结果:/E:/Code_Factory/WANWAN/bin/resource/res.txt  (../ Code_Factory/WANWAN/.. 是java project所在的路径)

           我们将这段代码打包成ResourceJar.jar ,并将ResourceJar.jar放在其他路径下(比如 c:\ResourceJar.jar)。然后另外创建一个java project并导入ResourceJar.jar,写一段调用jar包中Resource类的测试代码:

Java代码 
  1. import java.io.IOException;  
  2. import edu.hxraid.Resource;  
  3. public class TEST {  
  4.     public static void main(String[] args) throws IOException {  
  5.         Resource res=new Resource();  
  6.         res.getResource();  
  7.     }  
  8. }  

           这时的运行结果是:file:/C:/ResourceJar.jar!/resource/res.txt

           我们成功的在运行时动态获得了res.txt的位置。然而,问题来了,你是否可以通过下面这样的代码来得到res.txt文件?
                      File f=new File("C:/ResourceJar.jar!/resource/res.txt");
            当然不可能,因为".../ResourceJar.jar!/resource/...."并不是文件资源定位符的格式 (jar中资源有其专门的URL形式:
jar:<url>!/{entry} )。所以,如果jar包中的类源代码用File f=new File(相对路径);的形式,是不可能定位到文件资源的。这也是为什么源代码1打包成jar文件后,调用jar包时会报出FileNotFoundException的症结所在了。

          (3) 我们不能用常规操作文件的方法来读取ResourceJar.jar中的资源文件res.txt,但可以通过Class类的getResourceAsStream()方法来获取 ,这种方法是如何读取jar中的资源文件的,这一点对于我们来说是透明的。我们将Resource.java改写成:

Java代码 
  1. //源代码3:  
  2. package edu.hxraid;  
  3. import java.io.*;  
  4. public class Resource {  
  5.     public void getResource() throws IOException{  
  6.         //返回读取指定资源的输入流  
  7.         InputStream is=this.getClass().getResourceAsStream("/resource/res.txt");   
  8.         BufferedReader br=new BufferedReader(new InputStreamReader(is));  
  9.         String s="";  
  10.         while((s=br.readLine())!=null)  
  11.             System.out.println(s);  
  12.     }  
  13. }  

           我们将java工程下/bin目录中的edu/hxraid/Resource.class和资源文件resource/res.txt一并打包进ResourceJar.jar中,不管jar包在系统的任何目录下,调用jar包中的Resource类都可以获得jar包中的res.txt资源,再也不会找不到res.txt文件了。

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326270379&siteId=291194637