springboot打包后resource文件找不到(不存在),ClassPathResource获取不到文件
问题
读取springboot的resourece中的文件,原代码如下:
private static String readFile(String path) throws IOException {
ClassPathResource resource = new ClassPathResource(path);
Path filePath = Paths.get(resource.getURI());
return Files.readString(filePath);
}
打包后运行,报错:
java.nio.file.FileSystemNotFoundException
at jdk.zipfs/jdk.nio.zipfs.ZipFileSystemProvider.getFileSystem(ZipFileSystemProvider.java:169)
at jdk.zipfs/jdk.nio.zipfs.ZipFileSystemProvider.getPath(ZipFileSystemProvider.java:155)
at java.base/java.nio.file.Path.of(Path.java:208)
at java.base/java.nio.file.Paths.get(Paths.java:97)
at org.example.hello.service.SeleniumExample.readFile(SeleniumExample.java:37)
at org.example.hello.service.SeleniumExample.translate_phone(SeleniumExample.java:86)
at org.example.hello.controller.CommonController.quora_phone(CommonController.java:49)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
解法
改成
private static String readFile(String path) throws IOException {
try (var inputStream = new ClassPathResource(path, SeleniumExample.class.getClassLoader()).getInputStream()) {
return new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
}
}
原因
你的项目打成jar包,打开jar包看看发现,文件路径变成了jar包路径\BOOT-INF\classes\文件。
如果直接用ClassPathResource resource = new ClassPathResource(path);,打印出来的文件路径是jar:file:/jar包路径!/BOOT-INF/classes!/文件。
IDEA调试的时候, 资源文件是存在于真实文件系统里的一个文件。
在jar包中, 它不是一个真实文件系统的文件。
为了能用统一的文件系统路径去表示 jar 内的文件, 用了!
!表示这个文件是一个压缩包, 之后的路径则为压缩包内的路径。
正常情况下的 getFile() 操作, 会得到一个 jar 包路径后面加上一个!号然后再拼接上包内路径的一个路径.
第一个!在jar包路径!,这是对的,因为jar包本质就是压缩包。
但是有第二个!,classes!,这就是报错的原因,classes不是压缩包,但是ClassPathResource给classes前面加上了!,那就找不到。
那么为什么ClassPathResource会给classes加上一个!呢?
jar 的入口类其实是 Spring Boot Launcher, 他会为每一个依赖创建一个 ClassLoader, 这样就可以让每个依赖自己读取自己的资源文件而互不冲突.
而用户自己的类是从 /BOOT-INF/classes 开始的, 用户自己的资源文件的根目录也在这里, 所以为了让用户能够正确读到自己的资源文件. 加载用户代码的那个 ClassLoader 的 classpath 从这里开始.
fat-jar 并不是 Java 官方标准, 所以 Java 认为所有 classpath 都是从 jar 的根目录开始的.
于是我们得到的文件路径, 将是 {用户代码根目录}!/{资源文件路径}
大概理解就是:SpringBoot定义了一个classpath。
classpath = classes文件夹 = 用户代码根目录 = 用户资源文件根目录
所以你获得的目录本来该是 classes\文件,但是因为{用户代码根目录}!/{资源文件路径} ,变成了classes!\文件,这就是多了个!的原因。
参考
https://www.hiczp.com/spring/spring-boot-wu-fa-jia-zai-classpathresource-wen-ti.html
https://blog.csdn.net/qq_30038111/article/details/116992160
https://blog.csdn.net/liuxiao723846/article/details/122966413
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐


所有评论(0)