你知道怎么从jar包里获取一个文件的内容吗
阅读原文时间:2021年10月30日阅读:1

目录

背景

项目里需要获取一个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();
    }
}