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.youkuaiyun.com/qq_30038111/article/details/116992160
https://blog.youkuaiyun.com/liuxiao723846/article/details/122966413

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值