前言
最近的工作中遇到了复杂的excel报表导出业务,采用的是用excel模板来实现该业务(可以规避大量勾画excel格式的代码),将excel的模板放在项目的resources目录,遇到相关的一些问题及解决方案。
1.问题过程
resources目录下模板的位置:
在本地调试时直接用相对路径(templates/xxx.xlsx)就可以获取到对应的模板信息,调试自测过程都非常easy并愉快。
程序打包发到测试环境上,导出excel都是空模板没有任何数据,查看日志发现是在读取resources目录下的模板报错空指针,关键报错日志如下:
java.io.FileNotFoundException: class path resource [templates/scenery.xlsx] cannot be resolved to absolute file path because it does not reside in the file system: jar:file:/xxx.jar!/BOOT-INF/classes!/templates/xxx.xlsx
从报错信息可以看出来打包(jar)模板的位置和打包之前位置已经不一样了,jar里面也是没有src这个目录,则相对路径去读取模板当然是拿不到的。
2. 解决方案
2.1 文件上传
将resources目录的文件上传到服务器上,不从resources目录下读取模板,有2种方式:
- 将文件上传到文件服务(如阿里云OSS),并获取到文件地址,通过流方式读取文件。这种方式需要额外的成本(文件服务器)。
- 在jar运行的服务器上创建目录并存放文件,然后程序里读取相对路径。这种方式不太推荐,不可控因素太多(如被误删、服务搬迁等)。
2.2 ClassPathResource
用ClassPathResource读取到resources目录下文件的流,具体代码如下:
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
<include>**/*.yml</include>
</includes>
<!--是否替换资源中的属性-->
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.*</include>
</includes>
<!--是否替换资源中的属性-->
<filtering>false</filtering>
</resource>
ClassPathResource resource = new ClassPathResource("templates/xxx.xlsx");
InputStream inputStream = resource.getInputStream();
但是我excel导出是用的easyPOI,模板导出不支持流,必须得是文件的路径,具体实现的代码如下:
/**
* 模板报表导出
* @param type http
* @param data 报表信息
*/
public static void templateExcelExport(Map<String, Object> data, String type, HttpServletResponse httpServletResponse) throws IOException {
//获取模板的位置
ReportExcelEnum reportExcelEnum = ReportExcelEnum.valueOf(type);
//获取项目的根目录
String rootPath = System.getProperty("user.dir");
//创建临时的模板存放文件
String path = rootPath +"/" + reportExcelEnum.getValue();
log.info("存放模板目录及模板文件::"+path);
//判断模板是否已存放在文件中
File file = new File(path);
if(!file.exists()){
log.info("不存在则创建文件:"+path);
ClassPathResource resource = new ClassPathResource(reportExcelEnum.getValue());
InputStream inputStream = resource.getInputStream();
saveTempFile(inputStream,file);
log.info("模板创建成功!");
}
//获取模板
TemplateExportParams params = new TemplateExportParams(path);
//获取到 Workbook
Workbook workbook = ExcelExportUtil.exportExcel(params, data);
//文件名
String fileName = reportExcelEnum.getName() + DateTimeFormatter.ofPattern("yyyyMMdd").format(LocalDateTime.now()) + ".xlsx";
//导出报表
export(httpServletResponse, workbook, fileName);
}
private static void saveTempFile(InputStream inputStream, File tempFile) throws IOException {
//如果文件的目录不存在
if(!tempFile.getParentFile().exists()){
//创建父目录
tempFile.getParentFile().mkdirs();
}
OutputStream os = new FileOutputStream(tempFile);
byte[] b = new byte[2048];
int length;
while ((length = inputStream.read(b)) > 0) {
os.write(b, 0, length);
}
os.flush();
os.close();
inputStream.close();
}
总结
本篇记录一下读取resources目录下文件问题和解决方案,希望能帮到你。