Do you know how to get the contents of a file from the jar package

catalogue

background

In the project, you need to obtain an excle file and modify its contents. This file is in the jar package. No attempt to read it is successful, but you think it can be done, because the configuration file in the project can be read, so you start the road of exploration.

Error code

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

To successfully call the above methods, I need to read the contents of one file and then write to another file

The parameters of withTemplate can be File path of String type, File type and InputStream type, as follows:

The question now is how to a correct parameter to make it work

Original writing

Because all projects run in the form of jar packages, you need to get the address of the files in the jar package

There are two methods to obtain resources. The first is Class.getResource(), and the second is ClassLoader.getResource()

So the first edition was written as follows:

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

The location of the file is as follows:

There is no problem with the local test, but the following error is reported after the package is run:
Caused by: java.io.FileNotFoundException: File 'file:/Users/xxx/spring-boot-study/target/classes/Template.xlsx' does not exist

Locate the following issues:

Writing test classes

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

There is no problem running directly like this, and the output results are as follows:

/Users/xxx/spring-boot-study/target/classes/Template.xlsx true
file:/Users/xxx/spring-boot-study/target/classes/Template.xlsx false

However, after the jar package is printed, the operation results are as follows:

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

It can be seen that documents exist when they are not packaged; But when the package runs, the file does not exist.

Find the reason

Because there is some configuration information in the config.xml file in the project, and it is read successfully, the content must be read in a way, so I looked at this part of the code.

The core code is as follows:

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

So I began to read how the class constructor of XMLConfiguration was initialized

The core code for reading the contents of the file is as follows:

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

It can be seen that instead of converting the URL to File, it directly uses InputStream in = url.openStream();

Final code

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

Added by simoesp on Sat, 30 Oct 2021 10:57:02 +0300