java获取jar包中的文件资源
- 一、问题示例
-
- 1.1 项目开发时
- 1.2 打包成jar后
- 二、解决方案
-
- 2.1 解决方法
- 2.2 实现
问题描述:
我们常常在代码中读取一些资源文件(比如图片,音乐,文本等等)。在单独运行的时候这些简单的处理不会有问题。但是当我们把代码打成一个jar包以后,即使将资源文件一并打包,这些东西也找不出来了。此时应该怎么办???
我们在开发时,读取项目内的资源文件时,可以直接获取该资源文件在文件系统中的绝对路径。但是,一旦导出为jar包,就无法获取到资源文件的绝对路径。打完jar包以后资源(例如xml)等将被放在jar包下的第一层,这时使用上述路径就会错误,无法访问到资源。
一、问题示例
1.1 项目开发时
TestPath 文件内容:
package pro;
public class TestPath {
public TestPath() {}
public static String getFilePath(String fileName){
return TestPath.class.getResource(fileName).getPath();
}
}
主访问工程的main函数调用:
public static void main(String[] args) {
String path = TestPath.getFilePath("data1.txt");
System.out.println("path:"+path);
File file = new File(path);
if(file.exists() && file.isFile()){
try {
InputStream inputStream = new FileInputStream(file);
System.out.println(inputStream);
} catch (Exception e) {
System.err.println("解析文件"+file.getName()+"异常:");
e.printStackTrace();
}
}
}
输出结果:
path:/D:/eclipse_powflow/workspace/TestPathPro/bin/pro/data1.txt
java.io.FileInputStream@6bc7c054
1.2 打包成jar后
打包成jar包,并在主工程中引用,如下:
再执行main函数,输出结果如下:
path:file:/D:/eclipse_powflow/workspace/Test/lib/TestPathPro.jar!/pro/data1.txt
我们能够看到,此时的路径并不是文件资源定位符的格式 (jar中资源有其专门的URL形式: jar:!/{entry} )。所以,如果jar包中的类源代码用File f=new File(相对路径);
的形式,是不可能定位到文件资源的。这也是为什么源代码打包成jar文件后,调用jar包时会报出FileNotFoundException的症结所在了。
我们不能用常规操作文件的方法来读取ResourceJar.jar中的资源文件res.txt,但可以通过Class类的getResourceAsStream()方法来获取 ,这种方法是如何读取jar中的资源文件的,这一点对于我们来说是透明的。如下节所示。
二、解决方案
2.1 解决方法
把资源打入jar包,无论资源文件在系统的什么路径下,jar包中的字节码程序都可以找到该包中的资源。我们可以用类装载器(ClassLoader)来做到这一点:
1、ClassLoader :
是类加载器的抽象类。它可以在运行时动态的获取加载类的运行信息。 可以这样说,当我们调用.jar中的Resource类时,JVM加载进Resource类,并记录下Resource运行时信息(包括Resource所在jar包的路径信息)。而ClassLoader类中的方法可以帮助我们动态的获取这些信息:
2、public URL getResource(String name):
查找具有给定名称的资源。资源是可以通过类代码以与代码基无关的方式访问的一些数据(图像、声音、文本等)。并返回资源的URL对象。
3、public InputStream getResourceAsStream(String name):
返回读取指定资源的输入流。这个方法很重要,可以直接获得jar包中文件的内容。
2.2 实现
1、在TestPath工具类中使用getClassLoader().getResourceAsStream获取资源流数据:
/**增加函数,替换getFilePath()函数,直接获取资源流数据*/
public static InputStream getFileInputStream(String fileName){
return TestPath.class.getClassLoader().getResourceAsStream(fileName);
}
注意:fileName如果是xml文件等,直接写文件名即可,例如原来是src/main/xxx.xml,现在直接写main/xxx.xml(src的下级目录)
2、测试打印内容:
主访问工程的main函数调用:
public static void main(String[] args) {
try {
InputStream fileIn = TestPath.getFileInputStream("pro/data1.txt");
BufferedReader reader = new BufferedReader(
new InputStreamReader(fileIn,
StandardCharsets.UTF_8));
String s = "";
while ((s=reader.readLine())!=null) {
System.out.println(s);
}
} catch (IOException e) {
e.printStackTrace();
}
}
输出结果: