打包部署后无法读取jar包里的文件(实测可行,Java中读取jar包中的文件)
Posted 石头StoneWang
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了打包部署后无法读取jar包里的文件(实测可行,Java中读取jar包中的文件)相关的知识,希望对你有一定的参考价值。
打包部署后无法读取jar包里的文件
Java中读取jar包中的文件
linux中无法读取jar包中的内容(windows可以的!),如何解决
一、背景
项目中免不了需要读取文件,如果文件用绝对路径读取,就需要配置或写死路径,非常不便。如果我们读取类路径上的文件,就不会这么麻烦。
比如要读取的文件位于类路径下 java/main/resources/myfile.txt,这个文件在项目打成jar包的时候,也会压缩在里头,非常方便。
用 this.getClass().getResource(resourcePath)
或 this.getClass().getResourceAsStream(resourcePath)
获得文件路径或输入流。
上面2个方法就是从类路径上读取文件的,但有个大坑,就是你在IDEA里调试得好好的,但打成jar包,启动项目后发现可能会无法读取到这个文件。详细如下
-
打成jar包在linux启动,但是读取输入流为null
-
打成jar包在windows启动,能读取到输入流 (很奇葩,linux不能windows居然能)
-
在 IDEA 里:能读取到这个输入流
怎么解决
我实际验证了,下面的方式可行。
二、如何解决
改成读取jar包的方式读取jar包内的文件
如下这个类,就可以读取到输入流。。
- JarFile 等是JDK自带的
- 两个参数,第一个是jar包的位置,第二个是要读取的文件在jar包内的路径
public static InputStream readInputStreamFromJar(String jarPath, String fileInJar) {
JarFile jarFile = null;
InputStream input = null;
try {
jarFile = new JarFile(jarPath);
JarEntry jarEntry = jarFile.getJarEntry(fileInJar);
input = jarFile.getInputStream(jarEntry);
return input;
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
// 千万别关闭 jarFile,即使在这里没有关闭 inputStream,但是关闭 jarFile相当于关闭了 inputStream,
// 会导致 inputStream 虽然不是null但是available=0即没有内容了!!!
// IOUtils.closeQuietly(jarFile);
}
}
上述接口如何传参?
按下面的方法获得需要的入参
- JarPathResult 不列出代码了,就是个装返回结果的类
private static JarPathResult getJarResult(String path) {
int firstMark = path.indexOf("!");
int length = "jar:file:".length();
String jarFile = path.substring(length, firstMark);// 截出来win是/D:/... ,但后续仍然正常运行
String fileInJar = path.substring(firstMark + "!/".length()).replace("!", "");// 去掉开头的!/开头
JarPathResult result = new JarPathResult();
result.setJarFile(jarFile);
result.setFileInJar(fileInJar);
return result;
}
getJarResult(path)的入参说明
入参说明:
传入类似下面的格式的路径
jar:file:/root/app/read-file-from-jar-0.0.1-SNAPSHOT.jar!/BOOT-INF/classes!/sub/subfile.txt
(即传入 `this.getClass().getResource("/sub/subfile.txt").toString()` 的值)
出参说明:
/root/app/read-file-from-jar-0.0.1-SNAPSHOT.jar
BOOT-INF/classes/sub/subfile.txt
补充:
对于
this.getClass().getResource("/sub/subfile.txt").toString()
**怎么知道要写成 /sub/subfile.txt ? **
你的文件放在 java/main/resources 下的,以这个为根路径,以/开头,定位到你的文件。
- 比如 java/main/resources/abc.txt 就写成 /abc.txt
- 又如 java/main/resources/aa/bb/abc.txt 就写成 /aa/bb/abc.txt。
注意:
- 如果你乱写一个文件不存在或者路径写错,就会导致 getResource 返回null,toString之前是最好判断非null的
- 在 IDEA 里跑会报错,因为IDEA 里获取的路径不是 jar:file:/ 开头的!!!可以判断一些,如果是 jar:file:/ 开头的用JAR包的方式读取,如果是 file:/ 开头,直接用 this.getClass().getResourceAsStream() 即可。
附录:关于 getResource 读取的路径
this.getClass().getResource(resourcePath)
返回URL,这个URL#toString() 后就得到一个路径
有以下文件,分别放在:
src/main/resources/myfile.txt
src/main/resources/sub/myfile.txt
设计了如下的代码,在IDEA里启动项目后请求接口。之后再打jar包,并分别在windows、linux中启动
@GetMapping("/getPath")
public Map<String, String> getPath() {
Map<String, String> map = new HashMap<>();
String resourcePath1 = "/myfile.txt";
String resourcePath2 = "/sub/subfile.txt";
URL filepathUrl1 = this.getClass().getResource(resourcePath1);
URL filepathUrl2 = this.getClass().getResource(resourcePath2);
String filepath1 = filepathUrl1 == null ? null : filepathUrl1.toString();
String filepath2 = filepathUrl2 == null ? null : filepathUrl2.toString();
map.put(resourcePath1 + " 的绝对路径是:", filepath1);
map.put(resourcePath2 + " 的绝对路径是:", filepath2);
return map;
}
读取的结果是:
可以自行观察路径的规律。
-
在IDEA 里运行:
-
file:/D:/DevFolder/code/hello/read-file-from-jar/target/classes/myfile.txt
-
file:/D:/DevFolder/code/hello/read-file-from-jar/target/classes/sub/subfile.txt
-
-
打包后再win里运行:
-
jar:file:/D:/DevFolder/code/hello/read-file-from-jar/target/read-file-from-jar-0.0.1-SNAPSHOT.jar!/BOOT-INF/classes!/myfile.txt
-
jar:file:/D:/DevFolder/code/hello/read-file-from-jar/target/read-file-from-jar-0.0.1-SNAPSHOT.jar!/BOOT-INF/classes!/sub/subfile.txt
-
-
打包后再linux里运行:
-
jar:file:/root/app/read-file-from-jar-0.0.1-SNAPSHOT.jar!/BOOT-INF/classes!/myfile.txt
-
jar:file:/root/app/read-file-from-jar-0.0.1-SNAPSHOT.jar!/BOOT-INF/classes!/sub/subfile.txt
-
总结:
- 直接在 IDEA 中运行,会得到
file:/...
- 打成jar包后运行,得到
jar:file:/...
jar:file:/...
的格式有两个!
号,信息被分成了3段- 第一段是jar包的位置,比如 /root/app/read-file-from-jar-0.0.1-SNAPSHOT.jar
- 第二段是classpath的路径,比如 /BOOT-INF/classes
- 第三段是文件真正在什么路径,比如 /myfile.txt 或者 /sub/subfile.txt
以上是关于打包部署后无法读取jar包里的文件(实测可行,Java中读取jar包中的文件)的主要内容,如果未能解决你的问题,请参考以下文章