How to read file in the Jar file

Since the directory is changed after we pack the java applications into the jar file (user.dir property will be changed in the system), it causes the application won’t be able to find the corresponding resources (images or property files).

 

Following two solutions will show how to read the corresponding files from the jar file.

Obtain the Jar file root directory:

String path = new File(FrameConfig.class.getResource(“/”).getFile()).getAbsolutePath();  

Obtaining the file input stream for the file which zipped in the JAR file

Reader reader = new InputStreamReader(FrameConfig.class.getResourceAsStream(“/res/uiConfig.xml”));  

 

I did try something like above for my application which need to read the dictionary file for use, and it did work out after I exported it as runnable JAR file.

(Export Runnable Jar is as follow)

1.Select Export from Project Properties

1. Select Export from Project Properties

2.Select Runnable Jar option

2. Runnable Jar option

3.Finish the JAR creation (Here, you have the option to package all dependencies into the generated jar.)

3. Finish the creation.

 

===============================Additional Explanation in CHN==================================

 

我们常常在代码中读取一些资源文件(比如图片,音乐,文本等等)。在单独运行的时候这些简单的处理当然不会有问题。但是,如果我们把代码打成一个jar包以后,即使将资源文件一并打包,这些东西也找不出来了。看看下面的代码:

Java代码 收藏代码

  1. //源代码1:
  2. package edu.hxraid;  
  3. import java.io.*;  
  4. public class Resource {  
  5. public void 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. }    

这段代码写在Eclipse建立的java Project中,其目录为:(其中将资源文件res.txt放在了bin目录下,以便打成jar包)
      1、src/
              src/edu/hxraid/Resource.java
      2、bin/
              bin/resource/res.txt
              bin/edu/hxraid/Resource.class

      很显然运行源代码1是能够找到资源文件res.txt。但当我们把整个工程打成jar包以后(ResourceJar.jar),这个jar包内的目录为:
              edu/hxraid/Resource.class
              resource/res.txt

而这时jar包中Resource.class字节码:ldc <String “bin/resource/res.txt”> [20] 将无法定位到jar包中的res.txt位置上。就算把bin/目录去掉:ldc <String “resource/res.txt”> [20] 仍然无法定位到jar包中res.txt上。

这主要是因为jar包是一个单独的文件而非文件夹,绝对不可能通过”file:/e:/…/ResourceJar.jar/resource/res.txt”这种形式的文件URL来定位res.txt。所以即使是相对路径,也无法定位到jar文件内的txt文件(读者也许对这段原因解释有些费解,在下面我们会用一段代码运行的结果来进一步阐述)。

那么把资源打入jar包,无论ResourceJar.jar在系统的什么路径下,jar包中的字节码程序都可以找到该包中的资源。这会是幻想吗?

      当然不是,我们可以用类装载器(ClassLoader)来做到这一点:

(1) ClassLoader 是类加载器的抽象类。它可以在运行时动态的获取加载类的运行信息。 可以这样说,当我们调用ResourceJar.jar中的Resource类时,JVM加载进Resource类,并记录下Resource运行时信息(包括Resource所在jar包的路径信息)。而ClassLoader类中的方法可以帮助我们动态的获取这些信息:
          ● public URL getResource(String name)
            查找具有给定名称的资源。资源是可以通过类代码以与代码基无关的方式访问的一些数据(图像、声音、文本等)。并返回资源的URL对象。
          ● public InputStream getResourceAsStream(String name);
             返回读取指定资源的输入流。这个方法很重要,可以直接获得jar包中文件的内容。

(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.     }  

     运行这段源代码结果:/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.     }  

这时的运行结果是: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.     }  

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

 

================================Bonus Points===================================

含有这种代码的程序在运行时,以类(类路径)为基准,而不依赖当前路径(System中的user.dir),

【注】:上面的路径“/res/uiConfig.xml”是相对于包的路径,如果写成“res/uiConfig.xml”,表示相对于类的路径:package/res/uiConfig.xml,这需要将图片和文本等文件的保存路径,和程序中指定的路径两者保持一致。

读取jar包中根Element(dom4j)

private static Element getRootElement(String path) {  

  1.     Element rootElement = null;  
  2.     Reader reader = null;  
  3. try {  
  4.         reader = new InputStreamReader(  
  5.                 FrameConfig.class.getResourceAsStream(path));  
  6.         rootElement = new SAXReader().read(reader).getRootElement();  
  7.     } catch (Exception e) {  
  8.         e.printStackTrace();  
  9.     } finally {  
  10.         closeStream(reader);  
  11.     }  
  12. return rootElement;  
  13. }  
  14. private static void closeStream(Closeable stream) {  
  15. if (stream != null)  
  16. try {  
  17.             stream.close();  
  18.         } catch (IOException e) {  
  19.             e.printStackTrace();  
  20.         }  
  21. }  
  22. public static void main(String[] args) {  
  23.     Element uiConfig = getRootElement(“/res/uiConfig.xml”);  

 

读取jar包中根Element(jdom)

private static Element getRootElement(String path) {  

  1.     Element rootElement = null;  
  2.     Reader reader = null;  
  3. try {  
  4.         reader = new InputStreamReader(  
  5.                 FrameConfig.class.getResourceAsStream(path));  
  6.         rootElement = new SAXBuilder().build(reader).getRootElement();  
  7.     } catch (Exception e) {  
  8.         e.printStackTrace();  
  9.     } finally {  
  10.         closeStream(reader);  
  11.     }  
  12. return rootElement;  

 

读取jar包中国际化资源文件

ResourceBundle resources = ResourceBundle.getBundle(  

  1. “res.i18n”, Locale.getDefault());  
  2. // 取得对应的值
  3. String value = resources.getString(key); 

 

读取jar包中的图片资源

public static Image getImage(String imageName) {  

  1.     InputStream inputStream = null;  
  2.     ByteArrayOutputStream outputStream = null;  
  3. try {  
  4.         inputStream = FrameConfig.class.getResourceAsStream(“/res/images/”
  5.                 + imageName);  
  6.         outputStream = new ByteArrayOutputStream();  
  7. byte buffer[] = new byte[1024];  
  8. int len = 0;  
  9. while ((len = inputStream.read(buffer)) != -1)  
  10.             outputStream.write(buffer, 0, len);  
  11. return Toolkit.getDefaultToolkit().createImage(  
  12.                 outputStream.toByteArray());  
  13.     } catch (Throwable th) {  
  14.         th.printStackTrace();  
  15.     } finally {  
  16.         closeStream(inputStream);  
  17.         closeStream(outputStream);  
  18.     }  
  19. return null;  
  20. }  
  21. public static void main(String[] args) {  
  22.     Image image = getImage(“add.gif”);  
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s