一、SpringBoot项目打包成jar后读取文件的大坑,使用ClassPathResource获取classpath下文件失败
java.io.FileNotFoundException: class path resource [WorldPayTemplate.txt] cannot be resolved to absolute file path because it does not reside in the file system: jar:file:/opt/app.jar!/BOOT-INF/classes!/WorldPayTemplate.txt
读取文件模本使用的是
ClassPathResource resource = new ClassPathResource("WorldPayTemplate.txt");
log.info("读取文件对象,{}",resource);
StringBuilder builder = new StringBuilder();
try {
File file = resource.getFile();
log.info("file对象,{}",file);
BufferedReader reader = new BufferedReader(new FileReader(file));
log.info("BufferedReader对象,{}",reader);
String line = null;
while ((line = reader.readLine()) != null) {
log.info("单行文件内容:{}",line);
builder.append(line);
}
log.info("文件内容:{}",builder);
} catch (IOException e) {
log.error("文件读取异常:{}",e);
throw new MallException(ErrorMessage.FILE_READ_ERROR.getErrorCode());
}
抛到测试环境出现文件找不到错误,本地测试没问题
二、解决方法
使用ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
String txt = "";
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resources = resolver.getResources("templates/layout/email.html");
Resource resource = resources[0];
//获得文件流,因为在jar文件中,不能直接通过文件资源路径拿到文件,但是可以在jar包中拿到文件流
InputStream stream = resource.getInputStream();
StringBuilder buffer = new StringBuilder();
byte[] bytes = new byte[1024];
try {
for (int n; (n = stream.read(bytes)) != -1; ) {
buffer.append(new String(bytes, 0, n));
}
} catch (IOException e) {
e.printStackTrace();
}
txt = buffer.toString();
三、出现这个问题的原因
其实这是一个jar包发布的大坑,使用getFile()的时候的坑,对 ClassPathResource resource = new ClassPathResource("WorldPayTemplate.txt");以及File file = resource.getFile();进行debug,结果返回的是一个Jar协议地址:jar:file:/xxx/xx.jar!/xxxx。
然后继续跟踪到getFile(java.net.URL, java.lang.String)中,有如下的判断:
public static File getFile(URL resourceUrl, String description) throws FileNotFoundException {
Assert.notNull(resourceUrl, "Resource URL must not be null");
if (!"file".equals(resourceUrl.getProtocol())) {
throw new FileNotFoundException(description + " cannot be resolved to absolute file path because it does not reside in the file system: " + resourceUrl);
} else {
try {
return new File(toURI(resourceUrl).getSchemeSpecificPart());
} catch (URISyntaxException var3) {
return new File(resourceUrl.getFile());
}
}
}
因为resourceUrl.getProtocol()不是file,而是 jar,这样就抛出了一个FileNotFoundException异常。
ResouceUtils.getFile()是专门用来加载非压缩和Jar包文件类型的资源,所以它根本不会去尝试加载Jar中的文件,要想加载Jar中的文件,只要用可以读取jar中文件的方式加载即可,比如 xx.class.getClassLoader().getResouceAsStream()这种以流的形式读取文件的方式,所以使用读取文件流就可以拿到了。