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