問題描述
在項目中,使用到了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("[文件名字].[文件格式]");
【其余解決方案思路】並沒有全部都試過,在理論上可以解決
- 使用絕對路徑(jar包的資源文件路徑會發生變化,后續分析的時候會說)
- 使用ClassPathResource獲得文件資源並獲取資源文件流
- 通過利用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); } - 還有一種是通過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} ,因此可以定位到文件資源。),因此就不可能獲得到真正的位置,自然也就會報文件沒有找到的異常信息了。
