你知道怎麼從jar包里獲取一個文件的內容嗎
- 2021 年 10 月 30 日
- 筆記
背景
項目里需要獲取一個excle文件,然後對其里的內容進行修改,這個文件在jar包里,怎麼嘗試都讀取不成功,但是覺得肯定可以做到,因為項目里的配置文件就可以讀取到,於是開始了探索之路。
報錯的代碼
ExcelWriter excelWriter = EasyExcel.write("to.xlsx").withTemplate(t).build();
我想要成功調用以上的方法,需要讀取一個文件的內容,然後寫入到另一個文件中
withTemplate
的參數可以是String類型
的文件路徑,File類型
和InputStream類型
的,具體如下:
現在的問題就是如何一個正確的參數,能使它工作
原先的寫法
因為項目都是以jar包的形式運行,所以需要獲取jar包里文件的地址
有兩種獲取資源的方法,第一種就是Class.getResource()
,第二種就是ClassLoader.getResource()
於是有了如下的第一版寫法:
public class Main {
public static void main(String[] args) throws IOException {
URL url = Main.class.getResource("/Template.xlsx");
File file = new File(url.getFile());
ExcelWriter excelWriter = EasyExcel.write("to.xlsx").withTemplate(file).build();
excelWriter.finish();
}
}
文件的位置如下:
本地測試是沒有問題的,但是一打包運行後就報如下的錯誤:
Caused by: java.io.FileNotFoundException: File 'file:/Users/xxx/spring-boot-study/target/classes/Template.xlsx' does not exist
定位到如下問題:
編寫測試類
public class JarFileMain {
public static void main(String[] args) {
printFile(JarFileMain.class.getResource("/Template.xlsx"));
}
private static void printFile(URL url) {
if (url == null) {
System.out.println("null null");
return;
}
File file1 = new File(url.getFile());
System.out.println(file1 + " " + file1.exists());
File file2 = new File(url.toString());
System.out.println(file2 + " " + file2.exists());
}
}
直接這樣運行是沒有問題的,輸出結果如下:
/Users/xxx/spring-boot-study/target/classes/Template.xlsx true
file:/Users/xxx/spring-boot-study/target/classes/Template.xlsx false
但是打成jar包後,運行結果如下:
java -cp /Users/xxx/spring-boot-study/target/spring-boot-study-1.0-SNAPSHOT.jar cn.eagleli.java.resource.JarFileMain
file:/Users/xxx/spring-boot-study/target/spring-boot-study-1.0-SNAPSHOT.jar!/Template.xlsx false
jar:file:/Users/xxx/spring-boot-study/target/spring-boot-study-1.0-SNAPSHOT.jar!/Template.xlsx false
可以看出沒有打成包的時候,文件是存在的;但是打包後運行時,文件就不存在了。
找原因
因為項目里的config.xml
文件里有一些配置信息的,而且它是讀取成功的,所以肯定是由辦法讀取到內容的,於是就看了這部分的代碼。
核心代碼如下:
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.XMLConfiguration;
import org.apache.commons.configuration.reloading.FileChangedReloadingStrategy;
public class ClassPathConfigPropertySource implements PropertySource {
private XMLConfiguration config;
public ClassPathConfigPropertySource() {
try {
config = new XMLConfiguration(ClassPathConfigPropertySource.class.getClassLoader().getResource("config.xml"));
config.setReloadingStrategy(new FileChangedReloadingStrategy());
} catch (ConfigurationException e) {
}
}
@Override
public void setProperty(String key, Object value) {
if (config != null) {
config.setProperty(key, value);
}
}
@Override
public Object getProperty(String key) {
if (config != null) {
return config.getProperty(key);
}
return null;
}
}
於是就開始閱讀XMLConfiguration
這個類構造函數是怎麼初始化的
讀取文件內容的核心代碼如下:
public abstract class AbstractFileConfiguration extends BaseConfiguration implements FileConfiguration {
public void load(URL url) throws ConfigurationException
{
if (sourceURL == null)
{
if (StringUtils.isEmpty(getBasePath()))
{
// ensure that we have a valid base path
setBasePath(url.toString());
}
sourceURL = url;
}
// throw an exception if the target URL is a directory
File file = ConfigurationUtils.fileFromURL(url);
if (file != null && file.isDirectory())
{
throw new ConfigurationException("Cannot load a configuration from a directory");
}
InputStream in = null;
try
{
in = url.openStream();
load(in);
}
catch (ConfigurationException e)
{
throw e;
}
catch (Exception e)
{
throw new ConfigurationException("Unable to load the configuration from the URL " + url, e);
}
finally
{
// close the input stream
try
{
if (in != null)
{
in.close();
}
}
catch (IOException e)
{
getLogger().warn("Could not close input stream", e);
}
}
}
}
可見它沒有把URL
轉為File
,而是直接用的InputStream in = url.openStream();
最終代碼
public class Main {
public static void main(String[] args) throws IOException {
URL url = Main.class.getResource("/Template.xlsx");
//File file = new File(url.getFile());
ExcelWriter excelWriter = EasyExcel.write("to.xlsx").withTemplate(url.openStream()).build();
excelWriter.finish();
}
}