EasyExcel解决数据解析问题
学习目标
实际场景业务对文件流进行落库操作
熟练掌握EasyExcel解析Excel文件中的数据并对内容进行解析操作
熟练掌握EasyExcel导出List数据成excel
学习内容
- EasyExcel 导入
- EasyExcel 导出
学习产出
- 完成基本的业务要求
业务描述
通过时间区间去固定的客户号去调用第三方接口,通过返回的Base64文件流对其进行解析,落库操作
页面上对落库的数据进行查询,展示并有导出等一系列业务操作
业务需求
- 根据时间区间对数据进行落库,时间区间具体到具体到天,不到时分秒
- 落库的数据需要有日志展示并展示每次区间都有对应日志的反补操作
- 当前客户第一次数据落库时需要自动落库一年的数据
- 当时间区间的数据超过
limit
条时,需要进行二分操作,取时间区间的中间一天,并分成两次请求去获取数据。以此类推,知道数据不超过limit
数量限制。limit
数量限制为nacos配置文件中可配置
代码逻辑
1、 触发方法条件,每当客户进行登录操作都去调用条件,并将此次执行配置为一条数据库配置,动态的去选择当前客户是否需求本业务
2、 执行方法,检查当前客户下是否有同步日志,如果有则按最后一次的时间结束为本次的起始时间做增量操作
3、执行方法,如果没有同步日志,则同步近一年的数据
4、获取时间区间的total,判断是否超过limit
没有超过则继续,超过了则拆分时间成两段,轮询这两段时间重新执行改方法。
5、将获取到的文件流落库
6、将生成的日志id作为参数放入队列中
7、利用队列对日志id对应的Base64文件流进行处理
注:当然不会这么简单,但是总体逻辑是这样的,设计具体其他业务,过程比这个复杂的多,无法具体描述
EasyExcel
主要还是需要看官方的需求文档官方文档:官方文档地址
导入
映射的主体必须需要对应上,在主体的Class中在成员变量上添加@ExcelProperty
注解,在Class类上添加@ExcelIgnoreUnannotated
注解
java
// 此处是监听器,可以在写个监听器来处理对应sheet里面的数据
InvoiceEasyExcelReadListener invoicelistener = null;
//读取的逻辑代码
try{
// 将base64文件流转为字节
byte[] bytes = Base64.decodeBase64(fileBase);
// 将字节转为可被easyExcel读取的输入Stream流
ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
// 将一些需要在业务中处理的mapper,或者参数传入进去,但是在监听器中需要有对应的构造方法
invoicelistener = new InvoiceEasyExcelReadListener(mapper0,mapper1,mapper2, 参数);
try {
/**
* read 读取方法 此处的参数为input流,该方法是重载方法。还可以使用其他的读取方法如 readSheet()
* excelType方法,读取文件的类型,参数为枚举,官方提供了csv,xls,xlsx三种
* sheet读取excel的sheet名字或者下标,此处为固定名字。因为是该excel为固定模板
* head 将sheet里面的行数据对应到哪个类对象上
* headRowNumber 从第几行开始
* registerReadListener 对应的监听器
* doRead 执行读取
*/
EasyExcel.read(bis1).excelType(ExcelTypeEnum.XLSX).sheet("汇总表").head(Invoice.class).headRowNumber(1).registerReadListener(invoiceItemEasyExcelReadListener).doRead();
}catch (Exception e2){
log.error(incomeId + "读取发票明细信息sheet报错", e2);
}
}catch (Exception e) {
// 处理失败的逻辑代码
log.error("easy错误原因", e);
}
对应的监听器中的处理逻辑
java
@Component
@Slf4j
public class InvoiceEasyExcelReadListener<T> extends AnalysisEventListener<T> {
@Resource
private Mapper0 mapper0 ;
@Resource
private Mapper1 mapper1;
@Resource
private Mapper2 mapper2;
private String 参数;
public int success = 0;
public int error = 0;
private Map<String, Invoice> maps = new HashMap<>();
public InvoiceEasyExcelReadListener(Mapper0 mapper0,Mapper1 mapper1,Mapper2 mapper2, String 参数) {
this.mapper0 = mapper0 ;
this.mapper1= mapper1;
this.mapper2= mapper2;
this.参数= 参数;
}
@Override
public void invoke(T data, AnalysisContext context) {
Invoice invoice = (Invoice) data;
//内部处理该行数据的逻辑,如果需要该sheet里面的参数对应可以定义一个map的成员变量,将唯一值作为key,value值根据自己定义
//maps.put("key",invoice);
// 也可以记录成功的行数与失败的行数 则需要在开头加参数 并且写出对应的get方法
try{
success++;
}catch(Exception e){
error++;
}
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
log.info("结束");
}
@Override
public boolean hasNext(AnalysisContext context) {
return super.hasNext(context);
}
public int getSuccessNum() {
return success;
}
public int getErrorNum() {
return error;
}
}
如果需要在外面获取处理结果的数据,直接通过监听器调用里面参数的get方法,例如:
java
//获取成功的行数
int success = InvoiceEasyExcelReadListener.getSuccessNum();
//获取失败的行数
int error = InvoiceEasyExcelReadListener.getErrorNum();
导出
导出的方式有多种例如自己定义模板,通过映射的实体类转成对应的Excel,@ExcelProperty
住注解映射,通过index来确定导出的下标行数,value里面的值对应表头
java
@ExcelProperty(value = "客户名称",index = 13)
private String custname;
核心介绍:
- 得到需要转成文件的List数据
- 导出的文件表头顺序,样式
- 是否为多sheet
- 等等
我用的方法里面的大致处理逻辑为:
java
File file = new File("document_file" + File.separator + fileName);
List<List<String>> dataList = new ArrayList<>();
//里面放需要的参数,按照顺序放入例如 student.getName(),student.getAge()
//第一个sheet
ExcelWriter excelWriter = EasyExcel.write(file).build();
dataList.add(ListUtils.newArrayList(param1,param2...));
//InvoiceExportParam里面是导出映射的实体类,需要用@ExcelIgnoreUnannotated修饰这个类,@ExcelProperty(value = "购方名称",index = 4)这样的格式修饰成员变量
invoiceExport = EasyExcel.writerSheet("主体").head(ExportParam.class).build();
excelWriter.write(dataList,invoiceExport);
//规则同上
WriteSheet invoiceItemExport = EasyExcel.writerSheet("明细").head(ItemExpportParam.class).build();
excelWriter.write(dataListItem,invoiceItemExport);
excelWriter.finish();
// 项目封装的工具栏,将文件转为Base64文件流,最终返回给前端,前端转成blob对象,进行导出下载操作
String fileBase64 = FileUtils.fileToBase64(file);
list.clear();
result.put("code", "0001");
result.put("fileBase", fileBase64);
// 删除临时文件
file.delete();
return result;
我采用的方法为不同的模板使用不同的实体类来对应,感觉有一些不太好的问题,印象中可以将dataList
手动加一行表头,为中文数据,之后再将list依次加入
对于表头第一行的样式,以及内容格式的样式,在官方文档中都有描述,可以去参考官方文档,以下是我设置格式的部分代码
java
WriteCellStyle headWriteCellStyle = new WriteCellStyle();
headWriteCellStyle.setFillForegroundColor(IndexedColors.RED.getIndex());
WriteFont headWriteFont = new WriteFont();
headWriteFont.setFontHeightInPoints((short)20);
headWriteCellStyle.setWriteFont(headWriteFont);
WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
WriteFont contentWriteFont = new WriteFont();
contentWriteFont.setFontHeightInPoints((short)11);
contentWriteCellStyle.setWriteFont(contentWriteFont);
HorizontalCellStyleStrategy horizontalCellStyleStrategy =
new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
以上代码都是从官方文档中 CV的
完结...