<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
在專案中讀取檔案時, 使用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); }
上面程式碼的核心就是: 範例化
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<String> readTxtFile(String fileName) { List<String> 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檔案如下
<!-- 資源拷貝外掛,實現在打包時自動拷貝java目錄下以及resources目錄下的xml的組態檔 --> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.xml</include> </includes> </resource> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.xml</include> <include>**/*.properties</include> <include>**/banner.txt</include> </includes> </resource> </resources>
打包後資原始檔截圖如下, 從該圖中可以看到 holiday.txt 沒有被打包進來
程式執行之後的錯誤截圖
我們修改下指定打包的設定 <include>**/*.txt</include>
這樣設定後, 我們就可以將類路徑下的所有txt 檔案打包進行專案中了, 打包之後檔案位置如下圖
或者我們可以去除專案中下面的程式碼設定, 這樣做會預設打包 resources 下面的所有檔案
<!-- 資源拷貝外掛,實現在打包時自動拷貝java目錄下以及resources目錄下的xml的組態檔 --> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.xml</include> </includes> </resource> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.xml</include> <include>**/*.properties</include> <include>**/*.txt</include> </includes> </resource> </resources>
修改pom檔案後, 重新打包後資原始檔(從這裡可以看到 holiday.txt 被打包進來 )
在專案內的檔案的讀取/下載時, 由於本地路徑和專案打包後的路徑不同. 出現找不到檔案的情況,我們只需要例化ClassPathResource(檔名)
物件. 然後呼叫getInputStream 來獲取資原始檔.就能獲取任意環境下專案內的檔案
如果想打算使用其他方式來獲取resources 目錄下的檔案, 可以參見 這篇部落格 .核心和上面問題分析差不多, 基本上都是通過類載入器來獲取資原始檔的輸入流進而找到這個檔案
到此這篇關於專案打包成jar後包無法讀取src/main/resources下檔案的解決的文章就介紹到這了,更多相關jar無法讀取src/main/resources檔案內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45