最近在写一个可执行jar的程序,程序中包含了2个资源包,一个是images,一个是files。问题来了,在Eclipse里开发的时候,当用File类来获取files下面的文件时,没有任何问题。但是当程序导出为Runnable Jar时,运行程序时抛出异常,File not found。我们来一探究竟。
首先是我的工程目录结构:
程序中访问images下的图片代码:
setIconImage(new ImageIcon(this.getClass().getResource(结果,在Eclipse中和导出的jar包,运行正常。注意此处没有使用File类。
"/images/icon.png")).getImage());
程序中用File类访问files文件夹下的资源代码:
File f = new File(InfoUtils.class.getClass().getResource("/files/news.ini").getPath());结果,在Eclipse中运作正常,导出的jar包,抛异常。
然后尝试了如下的方法在jar包中来获取news.ini,结果宣告失败。
System.out.println("*************path test**************");我们来分析下,主要是因为jar包是一个单独的文件而非文件夹,绝对不可能通过"file:/e:/.../xxx.jar/files
System.out.println(InfoUtils.class.getResource("").getPath());
System.out.println(InfoUtils.class.getResource("/").getPath());
System.out.println(InfoUtils.class.getClassLoader().getResource(""));
System.out.println(new File("/files").getAbsolutePath());
System.out.println(new File("/files").getPath());
System.out.println(new File("/files").getPath());
System.out.println(new File("").getAbsolutePath());
System.out.println(new File("").getCanonicalPath());
System.out.println(System.getProperty("java.class.path"));
System.out.println("*************path test**************");
那么把资源打入jar包,无论ResourceJar.jar在系统的什么路径下,jar包中的字节码程序都可以找到该包中的资源。这会是幻想吗?当然不是,我们可以用类装载器(ClassLoader)来做到这一点:
它可以在运行时动态的获取加载类的运行信息。
import java.io.*;
import java.net.URL;
public class Resource {
public void getResource() throws IOException{
//查找指定资源的URL,其中res.txt仍然开始的bin目录下
URL fileURL=this.getClass().getResource("/resource/res.txt");
System.out.println(fileURL.getFile());
}
public static void main(String[] args) throws IOException {
Resource res=new Resource();
res.getResource();
}
}
运行这段源代码结果:/E:/Code_Factory/WANWAN/bin/resource/res.txt
我们将这段代码打包成ResourceJar.jar,并将ResourceJar.jar放在其他路径下(比如 c:\ResourceJar.jar)。然后另外创建一个javaproject并导入ResourceJar.jar,写一段调用jar包中Resource类的测试代码:
import java.io.IOException;这时的运行结果是:file:/C:/ResourceJar.jar!/resource/res.txt。
import xx.Resource;
public class TEST {
public static void main(String[] args) throws IOException {
Resource res=new Resource();
res.getResource();
}
}
我们成功的在运行时动态获得了res.txt的位置。然而,问题来了,你是否可以通过下面这样的代码来得到res.txt文件?
File f=newFile("C:/ResourceJar.jar!/resource/res.txt");
(3) 我们不能用常规操作文件的方法来读取ResourceJar.jar中的资源文件res.txt,但可以通过Class类的getResourceAsStream()方法来获取 ,这种方法是如何读取jar中的资源文件的,这一点对于我们来说是透明的。我们将Resource.java改写成:
import java.io.*;
public class Resource {
public void getResource() throws IOException{
//返回读取指定资源的输入流
InputStream is=this.getClass().getResourceAsStream("/resource/res.txt");
BufferedReader br=new BufferedReader(new InputStreamReader(is));
String s="";
while((s=br.readLine())!=null)
System.out.println(s);
}
}
我们将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
ClassLoaderUtil.getResourceAsStream(