项目打包成jar后包无法读取src/main/resources下文件的解决

网友投稿 1044 2022-08-06


项目打包成jar后包无法读取src/main/resources下文件的解决

目录一、项目场景二、问题描述发现问题分析问题为什么使用 ClassPathResource 后, 可以找到打包后的文件路径?三、解决方案方案一方案二意外出现总结

一、项目场景

在项目中读取文件时, 使用new File() 出现的一个坑以及解决流程这种问题不仅在本地文件读取时会遇到, 而且在下载项目下 (例如: src/main/resources目录下) 的文本时, 也会遇到,

二、问题描述

发现问题

原来代码该代码功能是利用 common.io 包下的FileUtils来读取文件, 放到一个字符串中

String s = FileUtils.readFileToString(new File("src/main/resources/holiday.txt"), "utf-8");

这种路径书写方式 new File("src/main/resources/holiday.txt") , 在本地运行没问题,但是打包之后在服务器中运行出现了问题. 下面是错误截图

可以看到在服务器中日志提示: java.io.FileNotFoundException: File 'holiday.txt' does not exist即: 在打包后, 一开始配置的路径src/main/resources下无法找到该文件

分析问题

项目在打包之后, 位于 resource目录下的文件, 最常见的就是各种Spring配置文件就会打包在 BOOT-INF/classes 目录下而FIle 在按照原来的文件路径src/main/resources/holiday.txt'去寻找, 必然找不到文件, 因此会报文件找不到的异常

在定位问题的过程中发现, 这里 提供了一个思路就是SpringBoot中所有文件都在jar包中,没有一个实际的路径,因此可以使用以下方式

/**

* 通过ClassPathResource类获取,建议SpringBoot中使用

* springboot项目中需要使用此种方法,因为jar包中没有一个实际的路径存放文件

*

* @param fileName

* @throws IOException

*/

public void function6(String fileName) throws IOException {

ClassPathResource classPathResource = new ClassPathResource(fileName);

InputStream inputStream = classPathResource.getInputStream();

getFileContent(inputStream);

hrdcfhrw }

为什么使用 ClassPathResource 后, 可以找到打包后的文件路径?

上面代码的核心就是: 实例化ClassPathResource 对象. 然后调用getInputStream 来获取资源文件

下面我们来分析这些代码在 ClassPathResource 在实例化时, 会初始化类加载器 classLoader 并将项目所用到的所有路径加载到类加载器 classLoader 中, 这些路径包括: java运行环境的jar, Maven 项目中的jar, 以及当前项目打包后的jar等(如下图)

而 classPathResource.getInputStream 在获取资源文件时, 因为上面我们初始化了一个classLoader.所以classLoader不为空, 因此会执行 getResourceAsStream 方法, 我们来追一下这个方法

getResourceAsStream 方法中的getResource是实际的业务处理方法, 我们继续深入

getResource 方法如下图, 实际的功能就是递归调用自己, 去不断遍历 parent 下的路径, 获取对应的资源文件那么 parent 又是谁呢? 我们继续往下看

看到这里我们豁然开朗, 这个神秘的 parent 就是类加载器classLoader!!!因此getResource 方法就是去不断遍历我们在ClassPathResource实例化时, 创建的类加载器下面的路径!!!(对应第1点)

三、解决方案

原来读取文件的代码如下

String s = FileUtils.readFileToString(new File("src/main/resources/holiday.txt"), "utf-8");

去查看 File 的构造函数, 看能否通过 InputStream 来构造从下图看是不行的

方案一

并且我们发现 org.apache.commons.io 下没有提供将 ClassPathResource 作为入参的读取文件的方法.因此我们必须手写读取文件的方法

手写的代码如下主要注意 Resource resource = new ClassPathResource(fileName); is = resource.getInputStream();

/**

* Java读取txt文件的内容

*

* @param fileName resources目录下文件名称(无需带目录)

* @return 将每行作为一个单位放到list中

*/

public static List readTxtFile(String fileName) {

List listContent = new ArrayList<>();

InputStream is = null;

InputStreamReader isr = null;

BufferedReader br = null;

String encoding = "utf-8";

try {

Resource resource = new ClassPathResource(fileName);

is = resource.getInputStream();

isr = new InputStreamReader(is, encoding);

br = new BufferedReader(isr);

String lineTxt = null;

while ((lineTxt = br.readLine()) != null) {

hrdcfhrw listContent.add(lineTxt);

}

} catch (IOException e) {

e.printStackTrace();

} finally {

try {

br.close();

isr.close();

is.close();

} catch (IOException e) {

e.printStackTrace();

}

}

return listContent;

}

方案二

这种方式对代码入侵较小, 核心还是利用 common.io 下的 FileUtils, 具体方法是利用FileUtils将ClassPathResource.getInputStream 得到的输入流复制到临时文件中, 然后读取这个临时文件这种方式缺点是: 需要创建临时文件, 如果待读取文件过大, 则重新创建文件和复制操作会消耗一定的空间和时间, 影响性能

//方式二 利用FileUtils将ClassPathResource.getInputStream 得到的输入流复制到临时文件中

Resource resource = new ClassPathResource("holiday.txt");

InputStream inputStream = resource.getInputStream();

File tempFile http://= File.createTempFile("temp", ".txt");

FileUtils.copyInputStreamToFile(inputStream, tempFile);

String s = FileUtils.readFileToString(tempFile, StandardCharsets.UTF_8);

意外出现

到这里又出现了一个问题, 就是我用的测试项目因为在 maven 里面指定了某些格式的文件. 如下配置因为指定了banner.txt 以及 xml 与 properties结尾的文件作为资源被打包. 所以文件 holiday.txt 运行后还是访问不到有问题的pom.xml文件如下

src/main/java

**/*.xml

src/main/resources

**/*.xml

**/*.properties

**/banner.txt

打包后资源文件截图如下, 从该图中可以看到 holiday.txt 没有被打包进来

程序运行之后的错误截图

我们修改下指定打包的配置 **/*.txt这样配置后, 我们就可以将类路径下的所有txt 文件打包进行项目中了, 打包之后文件位置如下图或者我们可以去除项目中下面的代码配置, 这样做会默认打包 resources 下面的所有文件

src/main/java

**/*.xml

src/main/resources

**/*.xml

**/*.properties

**/*.txt

修改pom文件后, 重新打包后资源文件(从这里可以看到 holiday.txt 被打包进来 )

总结

在项目内的文件的读取/下载时, 由于本地路径和项目打包后的路径不同. hrdcfhrw出现找不到文件的情况,我们只需要例化ClassPathResource(文件名) 对象. 然后调用getInputStream 来获取资源文件.就能获取任意环境下项目内的文件

如果想打算使用其他方式来获取resources 目录下的文件, 可以参见 这篇博客 .核心和上面问题分析差不多, 基本上都是通过类加载器来获取资源文件的输入流进而找到这个文件


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:java 方法与数组基础使用详解(java基础知识点)
下一篇:Java 继承与多态超详细梳理(java是什么)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~