最近在写一个可执行jar的程序,程序中包含了2个资源包,一个是images,一个是files。问题来了,在Eclipse里开发的时候,当用File类来获取files下面的文件时,没有任何问题。但是当程序导出为Runnable Jar时,运行程序时抛出异常,File not found。我们来一探究竟。

首先是我的工程目录结构:

程序中访问images下的图片代码:

  1. setIconImage(new ImageIcon(this.getClass().getResource(
  2. "/images/icon.png")).getImage());

结果,在Eclipse中和导出的jar包,运行正常。注意此处没有使用File类。

程序中用File类访问files文件夹下的资源代码:

  1. File f = new File(InfoUtils.class.getClass().getResource("/files/news.ini").getPath());

结果,在Eclipse中运作正常,导出的jar包,抛异常。

然后尝试了如下的方法在jar包中来获取news.ini,结果宣告失败。

  1. System.out.println("*************path test**************");
  2. System.out.println(InfoUtils.class.getResource("").getPath());
  3. System.out.println(InfoUtils.class.getResource("/").getPath());
  4. System.out.println(InfoUtils.class.getClassLoader().getResource(""));
  5. System.out.println(new File("/files").getAbsolutePath());
  6. System.out.println(new File("/files").getPath());
  7. System.out.println(new File("/files").getPath());
  8. System.out.println(new File("").getAbsolutePath());
  9. System.out.println(new File("").getCanonicalPath());
  10. System.out.println(System.getProperty("java.class.path"));
  11. System.out.println("*************path test**************");

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

那么把资源打入jar包,无论ResourceJar.jar在系统的什么路径下,jar包中的字节码程序都可以找到该包中的资源。这会是幻想吗?当然不是,我们可以用类装载器(ClassLoader)来做到这一点:

(1) ClassLoader是类加载器的抽象类。

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

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

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

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

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

  1. import java.io.IOException;
  2. import xx.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=newFile("C:/ResourceJar.jar!/resource/res.txt");
当然不可能,因为".../ResourceJar.jar!/resource/...."并不是文件资源定位符的格式(jar中资源有其专门的URL形式:jar:<url>!/{entry} )。所以,如果jar包中的类源代码用Filef=newFile(相对路径);的形式,是不可能定位到文件资源的。这也是为什么源代码1打包成jar文件后,调用jar包时会报出FileNotFoundException的症结所在了。

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

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

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

后话:

当然现在有jar包可以直接用于解决这个问题。

当然这种工作,前人也早已经研究过了。XWork中有个工具类,叫做ClassLoaderUtil,可以深入读取jar包中的资源文件。Struts2就是用这个工具类读取所有的plugin中的struts-default.xml的。例如用户可以下载xwork-core-2.1.4-jdk14.jar,里面包括有一个类ClassLoaderUtil.java,其中的静态函数

getResourceAsStream(String resourceName,Class callingClass)也可以解决这个问题。

ClassLoaderUtil.getResourceAsStream("wei.txt",String.class);

读取Jar包中的资源问题探究的更多相关文章

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

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

  2. Java如何获取当前的jar包路径以及如何读取jar包中的资源

    写作业的时候要输出一个record.dat文件到jar包的同级目录,但是不知道怎么定位jar包的路径.百度到的方法不很靠谱,所以在这里记录一下. 一:使用类路径 String path = this. ...

  3. 解决springboot读取jar包中文件的问题

    转载自: https://www.oschina.net/question/2272552_2269641 https://stackoverflow.com/questions/25869428/c ...

  4. springmvc获取jar中的静态资源与jar包中的资源互相引用问题

    1.首先看jar中的文件位置 2.在web工程中引用该jar 并且在springmvc文件中配置路径 如果有多个路径可用逗号隔开 3.在web工程找jsp页面如何引用 这样就可以了 关于jar中的资源 ...

  5. java读取jar包中的文件

    随手写了一个java小工具,maven打包成功后,发现工具总是读不到打在jar包中的文件信息,要读取的文件位于 /src/main/resources 目录下,打包成功后,文件就在jar包中根目录下, ...

  6. 使用NetBeans生成jar包,并在jar包中添加资源

    在NetBeans中,执行Clean and Build便可得到jar文件 若要在jar中添加资源,先用压缩软件打开jar,然后将资源拖进当前归档文件即可 使用Class.getResource(St ...

  7. springboot jar启动 读取jar包中相对路径文件报错

    jar包启动后读取相对路径文件报异常: Caused by: java.io.FileNotFoundException: class path resource [***.***] cannot b ...

  8. 通过 getResources 找不到jar包中的资源和目录的解决方法

    http://my.oschina.net/sub/blog/184074 今天碰到一个怪问题: 原本跑的好好的代码,打成 jar 包就不能运行了. 问题出在,代码中有一段自动扫描 classpath ...

  9. 解决SpringBoot jar包中的文件读取问题

    前言 SpringBoot微服务已成为业界主流,从开发到部署都非常省时省力,但是最近小明开发时遇到一个问题:在代码中读取资源文件(比如word文档.导出模版等),本地开发时可以正常读取 ,但是,当我们 ...

随机推荐

  1. 实现图片的2次缩放后再进行candy边缘检测

    //实现图片的2次缩放后再进行candy边缘检测//Author:SD//Date:2015-9-27#include "cv.h"#include "highgui.h ...

  2. Android之BroadcastReceiver 监听系统广播

    绑定广播有两种方式 一.配置文件绑定,在程序未启动也能监听 二.代码方式绑定,在程序启动后才能监听 1.绑定和取消绑定广播 public class MainActivity extends Acti ...

  3. Android 开发中常用 ADB 命令总结

    adb 的全称为 Android Debug Bridge,就是起到调试桥的作用.通过 adb 我们可以在 Eclipse 中方便通过 DDMS 来调试 Android 程序,说白了就是 debug ...

  4. HTMLParser使用详解(3)- 通过Filter访问内容

    HTMLParser遍历了网页的内容以后,以树(森林)结构保存了结果.HTMLParser访问结果内容的方法有两种.使用Filter和使用Visitor. (一)Filter类顾名思义,Filter就 ...

  5. 计算机硬件——pci卡图片

    搞IT的还是软硬都要了解的好.1. PCI插槽 2. PCI-E 3. PCI-X 4. mini PCI  

  6. 如何构建日均千万PV Web站点

    http://www.cnblogs.com/xiaocen/p/3723839.html http://www.cnblogs.com/xiaocen/p/3726763.html http://w ...

  7. [ActionScript 3.0] AS3 实现XML转换成JSON

    package com.fylib.util { /** * @author Frost.Yen * @E-mail 871979853@qq.com * @create 2015-6-18 下午2: ...

  8. 写代码的自动提示是怎么出来的...我的WebStorm中不能自动提示Bootstrap中的样式呢

    首先开启自动提示 File -> Settings ->Editor ->Code Completion ->Preselect the first suggestion:,将 ...

  9. UITableViewCell之微博篇

    微博篇 本应用所涉及的知识点: 1.UITableView 中的cell 2.模型的创建 3.MJExtension第三方框架的使用 需求分析 1.界面分析 微博界面 界面控件分析: 整个页面 1.不 ...

  10. 使用django表单,使网页添加上传文件,并分析文件。

    开发环境是: apache + python + django+ eclipse(开发环境) 欲达到目的: 在网页上,添加上传文件控件.然后读取csv文件,并分析csv文件. 操作步骤: django ...