解決Springboot專案打成jar包後獲取resources目錄下的檔案報錯的問題

2023-07-03 21:00:26

前幾天在專案讀取resources目錄下的檔案時碰到一個小坑,明明在本地是可以正常執行的,但是一發到測試環境就報錯了,說找不到檔案,報錯資訊是:class path resource [xxxx] cannot be resolved to absolute file path because it does not reside in the file system: jar:file:xxxx.jar!/BOOT-INF/classes!xxxx。

看了半天程式碼感覺沒有問題,於是懷疑是打成專案jar包後和原專案存在差異導致的。於是我把的專案打成jar包,在本地直接偵錯jar,果然發現問題所在。下面我將以一個自己的測試專案api-test替代原來的公司專案來講述一下排查過程。

一、專案程式碼

GetResourceTest:

public class GetResourceTest {
    public InputStream getResource1() throws IOException {
        File file = new DefaultResourceLoader().getResource("/template/qiankuan.ftl").getFile();
        return Files.newInputStream(file.toPath());
    }


    public InputStream getResource2() throws IOException {
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        Resource[] resources = resolver.getResources("/template/qiankuan.ftl");
        Resource resource = resources[0];
        return resource.getInputStream();
    }
}

TestController:

@RestController
@RequestMapping("/test")
public class TestController {
    @GetMapping(value = "/getResource")
    @ResponseBody
    public void getResource() throws IOException {
        GetResourceTest getResourceTest = new GetResourceTest();
        getResourceTest.getResource1();
    }
}

二、排查過程

1、首先使用Maven的install命令將專案打成jar包

命令執行成功後再target目錄下就生成了jar包

2、在Termininal裡cd到target目錄下,然後執行下面的程式碼,9992可以替換成其他埠

java -jar -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=9992 api-test-1.0.0-SNAPSHOT.jar

執行成功如下

3、新增遠端偵錯

依次點選選單Run,點選Edit Configurations,點選+,點選Remote JVM Debug,埠後改成剛剛設定的9992。

4、請求介面

請求測試介面,9991是專案原來的埠

127.0.0.1:9991/test/getResource

5、請求結果

請求果然報錯了,報錯和之前測試環境的報錯一摸一樣。其實我們通過這個報錯已經可以大致上看出問題了。。。

6、斷點偵錯

在請求的入口打上斷點開始斷點偵錯

通過斷點偵錯也可以看到這個檔案地址在原來的地址/template/qiankuan.ftl 前拼接了jar:file:/D:/Project/test/fhey-test/api-test/target/api-test-1.0.0-SNAPSHOT.jar!/BOOT-INF/classes!成為了jar:file:/D:/Project/test/fhey-test/api-test/target/api-test-1.0.0-SNAPSHOT.jar!/BOOT-INF/classes!/template/qiankuan.ftl。

然後在後面一段程式碼中,resourceUrl.getProtocol()的返回結果是"jar"而不是"file", 被判定為不是檔案然後丟擲了一個FileNotFoundException異常。

三、解決方法

ResouceUtils.getFile()是專門用來載入非壓縮和Jar包檔案型別的資源,所以它根本不會去嘗試載入Jar中的檔案,要想載入Jar中的檔案,只要用可以讀取jar中檔案的方式載入即可,比如 可以採用ClassPathResource這種以流的形式讀取檔案的方式或者PathMatchingResourcePatternResolver來讀取檔案。

ClassPathResource classPathResource = new ClassPathResource("/template/qiankuan.ftl" );
InputStream inputStream = classPathResource.getInputStream();

或者

ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resources = resolver.getResources("/template/qiankuan.ftl");
Resource resource = resources[0];
InputStream inputStream = resource.getInputStream();