项目打包成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")
, 在本地运行没问题,但是打包之后在服务器中运行出现了问题. 下面是错误截图
![项目打包成jar后包无法读取src/main/resources下文件的解决](https://img.it610.com/image/info11/39d32bc3984049348190610d9d0dc4fe.jpg)
文章图片
可以看到在服务器中日志提示:
java.io.FileNotFoundException: File 'holiday.txt' does not exist
即: 在打包后, 一开始配置的路径
src/main/resources
下无法找到该文件分析问题
【项目打包成jar后包无法读取src/main/resources下文件的解决】项目在打包之后, 位于 resource目录下的文件, 最常见的就是各种Spring配置文件就会打包在
BOOT-INF/classes
目录下而FIle 在按照原来的文件路径
src/main/resources/holiday.txt'
去寻找, 必然找不到文件, 因此会报文件找不到的异常![项目打包成jar后包无法读取src/main/resources下文件的解决](https://img.it610.com/image/info11/a0f02230b126411cb50371a677ecca22.jpg)
文章图片
在定位问题的过程中发现, 这里 提供了一个思路
就是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); }
为什么使用 ClassPathResource 后, 可以找到打包后的文件路径?
上面代码的核心就是: 实例化下面我们来分析这些代码ClassPathResource
对象. 然后调用getInputStream
来获取资源文件
在
ClassPathResource
在实例化时, 会初始化类加载器 classLoader
并将项目所用到的所有路径加载到类加载器 classLoader
中, 这些路径包括: java运行环境的jar, Maven 项目中的jar, 以及当前项目打包后的jar等(如下图)![项目打包成jar后包无法读取src/main/resources下文件的解决](https://img.it610.com/image/info11/37c504d169b04b859381fd9c57009a27.jpg)
文章图片
而
classPathResource.getInputStream
在获取资源文件时, 因为上面我们初始化了一个classLoader
.所以
classLoader
不为空, 因此会执行 getResourceAsStream
方法, 我们来追一下这个方法![项目打包成jar后包无法读取src/main/resources下文件的解决](https://img.it610.com/image/info11/23c297c24f234ccd8a637103ac3a0f90.jpg)
文章图片
getResourceAsStream
方法中的getResource
是实际的业务处理方法, 我们继续深入![项目打包成jar后包无法读取src/main/resources下文件的解决](https://img.it610.com/image/info11/ac065db5081e4e3ea4d32ecfd28fa474.jpg)
文章图片
getResource
方法如下图, 实际的功能就是递归调用自己, 去不断遍历 parent
下的路径, 获取对应的资源文件那么
parent
又是谁呢? 我们继续往下看![项目打包成jar后包无法读取src/main/resources下文件的解决](https://img.it610.com/image/info11/4fbdcb99604d401081e37470f1ef01ac.jpg)
文章图片
看到这里我们豁然开朗, 这个神秘的
parent
就是类加载器classLoader
!!!因此
getResource
方法就是去不断遍历我们在ClassPathResource
实例化时, 创建的类加载器下面的路径!!!(对应第1点)![项目打包成jar后包无法读取src/main/resources下文件的解决](https://img.it610.com/image/info11/7f2d639162e84b2883c40cb71f964ea0.jpg)
文章图片
三、解决方案 原来读取文件的代码如下
String s = FileUtils.readFileToString(new File("src/main/resources/holiday.txt"), "utf-8");
去查看 File 的构造函数, 看能否通过
InputStream
来构造从下图看是不行的
![项目打包成jar后包无法读取src/main/resources下文件的解决](https://img.it610.com/image/info11/a571b9b7a65e46edb43525467a81b75e.jpg)
文章图片
方案一
并且我们发现
org.apache.commons.io
下没有提供将 ClassPathResource
作为入参的读取文件的方法.因此我们必须手写读取文件的方法
![项目打包成jar后包无法读取src/main/resources下文件的解决](https://img.it610.com/image/info11/0d4abc36d936493f9f9e6258422acff2.jpg)
文章图片
手写的代码如下
主要注意
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) {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 = 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 没有被打包进来
![项目打包成jar后包无法读取src/main/resources下文件的解决](https://img.it610.com/image/info11/a0f02230b126411cb50371a677ecca22.jpg)
文章图片
程序运行之后的错误截图
![项目打包成jar后包无法读取src/main/resources下文件的解决](https://img.it610.com/image/info11/74c8b1a08ef44767a6ccc99bd36017e9.jpg)
文章图片
我们修改下指定打包的配置
**/*.txt
这样配置后, 我们就可以将类路径下的所有txt 文件打包进行项目中了, 打包之后文件位置如下图
或者我们可以去除项目中下面的代码配置, 这样做会默认打包 resources 下面的所有文件
src/main/java **/*.xml src/main/resources **/*.xml **/*.properties **/*.txt
修改pom文件后, 重新打包后资源文件(从这里可以看到 holiday.txt 被打包进来 )
![项目打包成jar后包无法读取src/main/resources下文件的解决](https://img.it610.com/image/info11/5a9e82d746444d2783d015f11f60a2fc.jpg)
文章图片
总结 在项目内的文件的读取/下载时, 由于本地路径和项目打包后的路径不同. 出现找不到文件的情况,我们只需要例化
ClassPathResource(文件名)
对象. 然后调用getInputStream 来获取资源文件.就能获取任意环境下项目内的文件如果想打算使用其他方式来获取resources 目录下的文件, 可以参见 这篇博客 .核心和上面问题分析差不多, 基本上都是通过类加载器来获取资源文件的输入流进而找到这个文件
到此这篇关于项目打包成jar后包无法读取src/main/resources下文件的解决的文章就介绍到这了,更多相关jar无法读取src/main/resources文件内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
推荐阅读
- “敏捷项目比传统项目快”这个说法对吗()
- 智汀家庭云的出现,能否打破苹果HomeKit,小米智能家居的神话。
- Docker部署项目完全使用指南(小结)
- 任正非答外媒51问最全版(|任正非答外媒51问最全版: 美国搞不垮华为,感谢给华为打广告,终端销量每月增长50%...)
- 在TypeScript项目中进行BDD测试
- 第十六周项目二----大数据集上排序算法性能的体验
- 使用jenkins实现前端自动化打包部署(Linux版本)
- Python+Tkinter打造签名设计工具
- Flutter入门教程(四)第一个flutter项目解析
- 常用网络命令总结