你知道怎么从jar包里获取一个文件的内容吗

时间:2023-03-09 18:19:33
你知道怎么从jar包里获取一个文件的内容吗

背景

项目里需要获取一个excle文件,然后对其里的内容进行修改,这个文件在jar包里,怎么尝试都读取不成功,但是觉得肯定可以做到,因为项目里的配置文件就可以读取到,于是开始了探索之路。

报错的代码

ExcelWriter excelWriter = EasyExcel.write("to.xlsx").withTemplate(t).build();

我想要成功调用以上的方法,需要读取一个文件的内容,然后写入到另一个文件中

withTemplate的参数可以是String类型的文件路径,File类型InputStream类型的,具体如下:

你知道怎么从jar包里获取一个文件的内容吗

现在的问题就是如何一个正确的参数,能使它工作

原先的写法

因为项目都是以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();
}
}

文件的位置如下:

你知道怎么从jar包里获取一个文件的内容吗

本地测试是没有问题的,但是一打包运行后就报如下的错误:
Caused by: java.io.FileNotFoundException: File 'file:/Users/xxx/spring-boot-study/target/classes/Template.xlsx' does not exist

定位到如下问题:

你知道怎么从jar包里获取一个文件的内容吗

编写测试类

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();
}
}