在代码中读取一些资源文件(比如图片,音乐,文本等等),在集成环境(Eclipse)中运行的时候没有问题。但当打包成一个可执行的jar包(将资源文件一并打包)以后,这些资源文件找不到,如下代码:

Java代码

[java] view
plain
copy

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

[java] view
plain
copy

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

[java] view
plain
copy

  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的症结所在了。

 注: 我们可以通过 new ImageIcon(类.class.getResource("XX.gif"))获得ImageIcon对象,但如果你要获得jar文件中的资源文件流,上面方法行不通。

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

Java代码

[java] view
plain
copy

  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. //InputStream is=当前类.class.getResourceAsStream("XX.config");
  9. BufferedReader br=new BufferedReader(new InputStreamReader(is));
  10. String s="";
  11. while((s=br.readLine())!=null)
  12. System.out.println(s);
  13. }
  14. }

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

(4) 上面方法只提供了从jar中读资源文件功能,没有提供改写jar中资源文件方法,

通过URL、URLConnection来得到jar中资源文件的输入流(InputStream)和输出流 (OutputStream),不仅可以从jar中读取资源文件而且还可以改写jar中资源文件

[java] view
plain
copy

  1. URL url = 类.class.getResource("XX.config");
  2. URLConnection  urlConnection=url.openConnection();
  3. //读资源文件
  4. InputStream inputStream=urlConnection.getInputStream();
  5. BufferedReader in=new BufferedReader(new InputStreamReader(inputStream,"utf-8"));
  6. //写资源文件
  7. OutputStream outputStream=urlConnection.getOutputStream();
  8. PrintStream printStream=new PrintStream(outputStream);

补充:刚测试了一下,利用URL、URLConnection来得到jar中资源文件的输出流 (OutputStream)不好使,提示java.net.UnknownServiceException: protocol doesn't support output,由于上述改写jar中资源文件方法不好使。

我们可以换个角度,不要死扣非得读写jar中的资源文件,只有当软件第一次启动程序时才读jar中的资源文件,并将其备份到用户主目录或用户当前目录中,以后读写用户目录中的资源文件即可。首先我们把资源文件打包到jar中,程序第一次程序启动时,仍然从jar中读资源文件信息(显然可以实现),并将所读取的信息保存到内存中(如:每次启动程序时,都将资源文件信息读到List或Map集合中保存),程序运行期间对资源文件的更新都是对List和Map的更新,当第一次关闭程序时,才将内存中保存的资源信息写到System.getProperty("user.home")用户主目录(或System.getProperty("user.dir")用户当前目录)中,并生成一个与jar中资源文件一模一样的文件,以后程序启动时都从用户目录的资源文件中读写。只有当用户目录中的资源文件不存在时(这里程序可以判断一下),才从jar中的资源文件读信息,因此jar中的资源文件只是起到原始备份作用。

当然,这样做会有2个问题(1):就是每次程序安装在一新机器上时,读取的资源文件信息都是jar中的原始资源信息,中途我们可能更新过,解决方法:将更新的资源文件再一次打包到jar中(2):由于每次对资源文件信息的更新都是对对应的内存数据的更新,只有在关闭程序时才写回到资源文件中,没有随改随写,这样就可能造成这样一种后果,如果程序不是很健壮,程序中途bug并无响应,此时只能强制关闭,务必造成更新的资源信息没有写回到资源文件中!!!解决方案:每次对内存中资源信息的更新后马上写回到文件中,利:保证了程序bug后资源文件仍然是最新的,弊:这样会造成频繁写文件操作,程序性能降低,(也不一定哦,试具体程序来看,由于它是资源文件,一般不会更新很频繁)。

(5)利用java.util.jar.JarFile?????没试过

从一个jar文件读取内容,然后写回一个jar文件。
首先通过一个JarFile file = new JarFile(fileName);
然后file.entries()可以得到一个jar文件内部元素的迭代器。同过这个迭代器我们可以获得所有的JarEntry,
然后通过InputStream is = jarFile.getInputStream(jarEntry);可以获得这个JarEntry的输入流。
最后将is的内容同过JarOutputStream out = new JarOutputStream(new FileOutputStream(distJar));的out的out.write(int data)方法写入目标jar文件。

java 从jar包中读取资源文件的更多相关文章

  1. (转)java 从jar包中读取资源文件

    (转)java 从jar包中读取资源文件 博客分类: java   源自:http://blog.csdn.net/b_h_l/article/details/7767829 在代码中读取一些资源文件 ...

  2. 深入jar包:从jar包中读取资源文件getResourceAsStream

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

  3. 【解惑】深入jar包:从jar包中读取资源文件

    [解惑]深入jar包:从jar包中读取资源文件 http://hxraid.iteye.com/blog/483115 TransferData组件的spring配置文件路径:/D:/develop/ ...

  4. [Java基础] 深入jar包:从jar包中读取资源文件

    转载: http://hxraid.iteye.com/blog/483115?page=3#comments 我们常常在代码中读取一些资源文件(比如图片,音乐,文本等等).在单独运行的时候这些简单的 ...

  5. JAR包中读取资源文件

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

  6. 深入jar包:从jar包中读取资源文件

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

  7. Jar中的Java程序如何读取Jar包中的资源文件

    Jar中的Java程序如何读取Jar包中的资源文件 比如项目的组织结构如下(以idea中的项目为例): |-ProjectName |-.idea/  //这个目录是idea中项目的属性文件夹 |-s ...

  8. 从jar包中读取资源

    package myspider; import java.io.UnsupportedEncodingException; /** * * @author mark */ public class ...

  9. 读取Jar包中的资源问题探究

    最近在写一个可执行jar的程序,程序中包含了2个资源包,一个是images,一个是files.问题来了,在Eclipse里开发的时候,当用File类来获取files下面的文件时,没有任何问题.但是当程 ...

随机推荐

  1. 余弦距离、欧氏距离和杰卡德相似性度量的对比分析 by ChaoSimple

      1.余弦距离 余弦距离,也称为余弦相似度,是用向量空间中两个向量夹角的余弦值作为衡量两个个体间差异的大小的度量. 向量,是多维空间中有方向的线段,如果两个向量的方向一致,即夹角接近零,那么这两个向 ...

  2. POJ 1503 Integer Inquiry 简单大数相加

    Description One of the first users of BIT's new supercomputer was Chip Diller. He extended his explo ...

  3. Distinct Substrings - spoj 694(不重复子串个数)

    题目大意:RT   分析:练手题目....后缀数组确实很强大.....多理解height数组, 切勿使用模版,后缀数组本身就有很多细节,多犯错更有利理解这个算法.   代码如下: ========== ...

  4. 2014年河南省第七届ACM大赛总结

    虽然大赛已经结束了两天,不过比赛的场景还是不断地在眼前回放,一遍遍,这次的比赛给了我很深刻的感悟还有教训. 刚开始比赛选择了贩卖武器那道题,也是全场到最后唯一没有被人做出来的一道题,策略的严重错误,大 ...

  5. JuliaSet&MandelBulb @ Maya&KK —— 4亿粒子的测试

    分形是数学里最美的一个话题之一,美在 无限的细节 在尺寸上的自相似 这两个特征造成的牛逼效果就是随便选择分形上的一个小坑或者小山包拉进后又是一个广阔的世界,而把这个世界中的一个小坑或者小山包拉进又™是 ...

  6. C# ToString格式大全

      C# 货币  2.5.ToString("C"); // ¥2.50 // D 10进制数 25.ToString("D5"); // 25000 // E ...

  7. 【javascript基础知识】javascript中的转义序列和特殊数值常量

    javascript的转义序列 \0 NUL字符(\u0000) \b 退格符(\u0008) \t 水平制表符(\u0009) \n 换行符(\u000A) \v 垂直制表符(\u000B) \f ...

  8. C#将数据以XML格式写入Excel

    本文转载:http://www.cnblogs.com/eflylab/archive/2008/09/21/1295580.html c#将数据导入Excel另类方法 今天公司突然给个Excel模版 ...

  9. sql server 获取每一个类别中值最大的一条数据

    /* 数据如下: name val memo a 2 a2(a的第二个值) a 1 a1--a的第一个值 a 3 a3:a的第三个值 b 1 b1--b的第一个值 b 3 b3:b的第三个值 b 2 ...

  10. 学习NodeJS第一天:node.js引言

    Node.JS 是资深 C 程序猿 Ryan Dahl(http://four.livejournal.com/)的作品,根据 Google 著名的开源 JavaScript 引擎 V8 来进行二次开 ...