解決SpringBoot jar包中的檔案讀取問題
前言
SpringBoot微服務已成為業界主流,從開發到部署都非常省時省力,但是最近小明開發時遇到一個問題:在程式碼中讀取資原始檔(比如word文件、匯出模版等),本地開發時可以正常讀取 ,但是,當我們打成jar包釋出到伺服器後,再次執行程式時就會丟擲找不到檔案的異常。
背景
這個問題是在一次使用freemarker模版引擎匯出word報告時發現的。大概說一下docx匯出java實現思路:匯出word的文件格式為docx,事先準備好一個排好版的docx文件作為模版,讀取解析該模版,將其中的靜態資源替換再匯出。
docx文件本身其實是一個壓縮的zip檔案,將其解壓過後就會發現它有自己的目錄結構。
問題
這個docx文件所在目錄如下圖所示:
在本地除錯時,我使用如下方式讀取:
import org.springframework.util.ResourceUtils;
public static void main(String[] args) throws IOException {
File docxTemplate = ResourceUtils.getFile("classpath:templates/docxTemplate.docx");
}
可以正常解析使用,但是打包釋出到beta環境卻不可用。丟擲異常如下:
java.io.FileNotFoundException: class path resource [templates/docxTemplate.docx] cannot be resolved to absolute file path because it does not reside in the file system: jar:file:/usr/local/subject-server.jar!/BOOT-INF/classes!/templates/docxTemplate.docx
顯而易見,這個異常告訴我們:沒有找到檔案,但是將jar包解壓過後,發現這個檔案是真真實實存在的。
那這到底是怎麼回事呢?這壓根難不倒我。我們要善於透過堆疊資訊看本質。通過仔細觀察堆疊資訊,我發現此時的檔案路徑並不是一個合法的URL(檔案資源定位符)。原來jar包中資源有其專門的URL形式: jar:
File f=new File("jar:file:……");
定位檔案,就會丟擲java.io.FileNotFoundException。
解決
雖然我們不能用常規操作檔案的方法來讀取jar包中的資原始檔docxTemplate.docx
public static void main(String[] args) throws IOException {
InputStream inputStream = WordUtil.class.getClassLoader().getResourceAsStream("templates/docxTemplate.docx");
}
拿到流之後,就可以將其轉換為任意一個我們需要的物件,比如File、String等等,此處我要獲取docxTemplate.docx下的目錄結構,因此我需要一個File物件,程式碼舉例如下:
import org.apache.commons.io.FileUtils;
public static void main(String[] args) throws IOException {
InputStream inputStream = WordUtil.class.getClassLoader().getResourceAsStream("templates/docxTemplate.docx");
File docxFile = new File("docxTemplate.docx");
// 使用common-io的工具類即可轉換
FileUtils.copyToFile(inputStream,docxFile);
ZipFile zipFile = new ZipFile(docxFile);
Enumeration<? extends ZipEntry> zipEntrys = zipFile.entries();
// todo 記得關閉流
}
結果
打包、釋出至beta環境,親測可用,問題完美解決。
本文可轉載,但需宣告原文出處。 程式設計師小明,一個很少加班的程式設計師。歡迎關注微信公眾號,獲取更多優質文章。