【踩坑記錄】關於jar包找不到resources目錄下文件的異常


問題描述

在項目中,使用到了resources文件下的數據文件,使用的是File方式:

  // ResourceUtils.CLASSPATH_URL_PREFIX 值為:"classpath:"
  // cityProperties.getPath(); 為要使用的文件地址
  String path = ResourceUtils.CLASSPATH_URL_PREFIX + cityProperties.getPath();
  File citys = ResourceUtils.getFile(path);
  if (!citys.exists()) {
    return;
  }
  ...

本地運行沒有問題,打成jar包后運行會有FileNotFoundException報錯(未找到文件):

java.io.FileNotFoundException: class path resource [xxx] cannot be resolved to absolute file path because it does not reside in the file system:
jar:file:/starcharge/starcharge.jar!/BOOT-INF/classes!/[xxx]

解決方案

【推薦方案】將File操作資源的方式替換為使用流的方式讀取資源

ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        // 這里了的例子是使用了 resources/city 文件夾下多個文件類型為 xxx.csv 的文件
        Resource[] resources = resolver.getResources("city/**.csv");
        if (resources.length < 1 ) {
            throw new IOException("讀取文件出錯");
        }
        System.out.println(resources.length);
        for (Resource resource : resources) {
            try (InputStream input = resource.getInputStream()) {
                List<String> allLines = IOUtils.readLines(input);
               ...
            }
           ...

        }

注意:如果你在getResources( ) 中寫入的只有文件夾名稱的話,會報數組越界的異常。
如果你使用的是單個文件則可以更改為:

// 注意這里的文件名字前不能加"/",否則也會出現上述異常
Resource resource = resolver.getResource("[文件名字].[文件格式]");

【其余解決方案思路】並沒有全部都試過,在理論上可以解決

  1. 使用絕對路徑(jar包的資源文件路徑會發生變化,后續分析的時候會說)
  2. 使用ClassPathResource獲得文件資源並獲取資源文件流
  3. 通過利用ClassLoader讀取Jar包內部文件,ClassLoader 是類加載器的抽象類。它可以在運行時動態的獲取加載類的運行信息。我們真正寫代碼的時候,是通過Class類中的getResource()getResourceAsStream()方法,這兩個方法會委托ClassLoader中的getResource()getResourceAsStream()方法 。
    public void getResource() throws IOException{  
         //返回讀取指定資源的輸入流  
         InputStream is=this.getClass().getResourceAsStream("/resource/res.txt");   
         BufferedReader br=new BufferedReader(new InputStreamReader(is));  
         String s="";  
         while((s=br.readLine())!=null)  
             System.out.println(s);  
     }  
    
  4. 還有一種是通過DefaultResourceLoader獲得文件資源保存一個緩存文件在項目中,然后去操作緩存文件(這種方式可能會出現:java.lang.IllegalArgumentException: URI is not hierarchical異常信息,在這里不做討論)

【方法2】【方法3】本質還是通過流去操作文件


原因分析

jar包中的類源代碼用File f=new File(相對路徑);的形式,是不可能定位到文件資源的。因為在本地運行時,src/main/下面的java和resources文件夾都被(編譯)打包到了target生產包的classes/目錄下,這個時候,classes這個文件夾,它就是我們要找的classpath。所以在本地運行是沒有問題的。

而在打成jar包以后,這里的結構就發生了變化。我們可以通過命令[jar包名稱].jar $ tree來查看jar包的結構。可以發現此時classes這個文件夾被放在了BOOT-INF文件夾下,所以通過File使用的相對路徑並不是文件資源定位符的格式(jar中的資源有其專門的URL形式:jar:<url>!/{entry} ,因此可以定位到文件資源。),因此就不可能獲得到真正的位置,自然也就會報文件沒有找到的異常信息了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM