表格内容如下

分析

  1. 该文档总共9个Sheet页
  2. 以Sheet0(客户交易结算月报)为例:表格总共分为4个部分(图中红色部分)。一、二部分数据为横向数据(字段名:字段值),三四部分为纵向集合。第四部分每一行数据下有合计栏(合计栏数据不读取)。

实现逻辑

1. 前期准备

1) Pom文件引入Easy Excel

    <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.0.5</version></dependency>

2)实现AnalysisEventListener

public class EasyExcelListener extends AnalysisEventListener<Object> {// 创建list集合封装最终的数据private List<Object> list = new ArrayList<>();// sheet页索引private int sheetNo = 0;@Overridepublic void invoke(Object t, AnalysisContext context) {// 读取excle内容int currentSheetNo = context.readSheetHolder().getSheetNo();if (currentSheetNo != sheetNo) {// 如果不根据sheet页索引更新状态重新创建list,list会反复添加前面的sheet页对象值list = new ArrayList<>();sheetNo = currentSheetNo;}list.add(t);}// 读取excel表头信息@Overridepublic void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {}// 读取完成后执行@Overridepublic void doAfterAllAnalysed(AnalysisContext analysisContext) {}/*** 获取表格内容(简单excel读取可用该方法)** @param obj 需要转化的实体* @param <T>* @return*/public <T> List<T> getList(Class<T> obj) {String jsonObj = JSONUtil.toJsonStr(list);return JSONUtil.toList(jsonObj, obj);}/*** 将表格转化为map集合(复杂excel读取用此方法)** @return map集合*/public List<LinkedHashMap> getListMap() {String jsonObj = JSONUtil.toJsonStr(list);return JSONUtil.toList(jsonObj, LinkedHashMap.class);}}

ps: 为什么用LinkHashMap接收excel表格内容?

读取文件内容时,在不确定接收实体的情况下,对象都是以LinkedHashMap进行存储的。图中可以看到,每个map的key,value是以列索引以及对应的行值进行存储。

3)Excel工具类

@Slf4j
public class EasyExcelUtil {private EasyExcelUtil() {}/*** 根据easyexcel注解给指定实体赋值** @param objects 读取的表格内容* @param clazz   需转化的实体* @param <T>     实体* @return 需转化的提示集合*/public static <T> List<T> convertList(List<LinkedHashMap> objects, Class<T> clazz) {List<T> results = new ArrayList<>(objects.size());try {Map<String, Field> objIndex = new HashMap<>();// 获取转化实体字段信息集合Field[] fields = clazz.getDeclaredFields();for (Field field : fields) {// 根据实体上Easy Excel的ExcelProperty注解中的索引值对应excel读取数据的值int index = field.getAnnotation(ExcelProperty.class).index();// 设置字段可编辑field.setAccessible(true);objIndex.put(String.valueOf(index), field);}T obj = null;for (LinkedHashMap o : objects) {obj = clazz.newInstance();for (Object key : o.keySet()) {// 如果表格索引与字段注解指定索引一样则赋值if (objIndex.containsKey(key)) {Object object = o.get(key);Object value = null;Field field = objIndex.get(key);if (ObjectUtil.isEmpty(object)) {continue;}Class<?> type = field.getType();String replace = object.toString();// 有特殊需要处理的字段类型则在此进行处理if (type == BigDecimal.class) {value = "--".equals(replace) ? null : new BigDecimal(replace.replace(",", ""));} else if (type == Integer.class) {// String强转Integer会报错,所以需要单独进行转化value = "--".equals(replace) ? null : Integer.valueOf(replace.replace(",", ""));} else {value = object;}field.set(obj, value);}}results.add(obj);}} catch (Exception e) {log.error("字段解析失败", e);Asserts.fail("字段解析失败:" + e.getMessage());}return results;}
}

参考实体:
@ExcelProperty注解必须有,且对应到字段值对应的列数。如果不确定列数可以先读取linkedlist 看看对应的索引值

@Data
@ApiModel("其他资金明细excel实体")
public class OtherFundDetailsExcelDTO {@ApiModelProperty(value = "发生日期")@ExcelProperty(value = "发生日期", index = 0)private String happenDate;@ApiModelProperty(value = "交易所")@ExcelProperty(value = "交易所", index = 2)private String exchange;@ApiModelProperty(value = "类型")@ExcelProperty(value = "类型", index = 4)private String type;@ApiModelProperty(value = "金额")@ExcelProperty(value = "金额", index = 6)private BigDecimal money;@ApiModelProperty(value = "备注")@ExcelProperty(value = "备注", index = 8)private String remark;
}

2. 编码

1)Controller获取文件内容

     @ApiOperation("导入")@PostMapping(value = "/excel/upload")public void upload(@ApiParam(name = "file", value = "file", required = true) @RequestParam(value = "file") MultipartFile file, HttpServletResponse response) throws IOException {// step1. 读取excel内容EasyExcelListener easyExcelListener = new EasyExcelListener();ExcelReaderBuilder read = EasyExcelFactory.read(file.getInputStream(), easyExcelListener);ExcelReader excelReader = read.build();// step2. 获取各个sheet页信息List<ReadSheet> sheets = excelReader.excelExecutor().sheetList();// step3. 获取各个Shhet页表格内容存于mapMap<Integer, List<LinkedHashMap>> sheetInfos = new HashMap<>(sheets.size());for (ReadSheet sheet : sheets) {Integer sheetNo = sheet.getSheetNo();excelReader.read(sheet);sheetInfos.put(sheetNo, easyExcelListener.getListMap());}iSettlementMothReportService.saveExcelInfo(sheetInfos);}

2) Service将读取内容转化为实体类(此代码仅提供两种excel表格读取方式,横向数据读取以及纵向集合读取)

public void saveExcelInfo(Map<Integer, List<LinkedHashMap>> sheetInfos) {SettlementMothReportSaveDTO settlementMothReportSaveDTO = new SettlementMothReportSaveDTO();for (Integer sheetNo : sheetInfos.keySet()) {List<LinkedHashMap> maps = sheetInfos.get(sheetNo);// 不同sheet页数据处理方式不同switch (sheetNo) {case 0:settlementMonthlyReport(maps, settlementMothReportSaveDTO);break;case 1:varietyCollects(maps, settlementMothReportSaveDTO);break;case 2:holdingsSubsidiary(maps, settlementMothReportSaveDTO);break;case 3:dealDetail(maps, settlementMothReportSaveDTO);break;default:break;}}iBzjBaseInfoService.saveSettlementMothReport(settlementMothReportSaveDTO);}
private void settlementMonthlyReport(List<LinkedHashMap> maps, SettlementMothReportSaveDTO settlementMothReportSaveDTO) {// 基本资料BzjBaseInfo baseInfo = new BzjBaseInfo();// 期货期权账户出入金明细List<LinkedHashMap> optionAccountsDetails = new ArrayList<>();// 是否基本资料boolean whetherBaseInfo = true;// 是否期货期权账户出入金明细boolean whetherOptionAccountsDetails = false;for (LinkedHashMap map : maps) {if (whetherBaseInfo) {// 基本资料whetherBaseInfo = dealBaseInfo(map, baseInfo);if (!whetherBaseInfo) {continue;}}if (map.containsValue(OptionAccountsDetailsConstant.OPTION_ACCOUNTS_DETAILS)) {whetherOptionAccountsDetails = true;}if (!whetherBaseInfo && whetherOptionAccountsDetails) {// 期货期权账户出入金明细whetherOptionAccountsDetails = dealOptionAccountsDetails(map, optionAccountsDetails);if (!whetherOptionsFuturesFunds) {continue;}}}if (!optionAccountsDetails.isEmpty()) {List<FuturesOptionAccountsDetailsExcelDTO> dtos = EasyExcelUtil.convertList(optionAccountsDetails, FuturesOptionAccountsDetailsExcelDTO.class);settlementMothReportSaveDTO.setOptionAccountsDetails(dtos);}settlementMothReportSaveDTO.setBzjBaseInfo(baseInfo);}

ps:为什么要用模块标题作为标识而不是索引?
因为以模块标题作为标识,如果上一个模块出现增删数据时,不影响本模块的读取。如果直接用表格行索引,那么表格内容出现变更时就无法灵活读取数据。

 /*** 处理基础资料** @param map      excel表格信息* @param baseInfo 基础资料信息* @return 是否基本资料*/private boolean dealBaseInfo(LinkedHashMap map, BzjBaseInfo baseInfo) {// 是否基础信息内容if (map.containsValue(BaseInfoConstant.BASE_INFO)) {return true;}// 根据表格内容分析,对象值在2,7列。合并单元格在读取之后会将值存在合并的第一列索引下,如合并A-C列,那A列会存储字段值,B、C列值为空。具体内容可见上面读取excel截图Object obj = map.get("2");String str = null;if (ObjectUtil.isNotEmpty(obj)) {str = obj.toString();}Object object = map.get("7");String value = null;if (ObjectUtil.isNotEmpty(object)) {value = object.toString();}// 通过本行的第一个字段名,获取本行的所有字段信息(后面的判断均是如此,常量类对应了各个字段名)if (map.containsValue("客户期货期权内部资金账户")) {baseInfo.setCusFuturesInnerFundAccount(str);baseInfo.setTransactionMonth(value);}if (map.containsValue(BaseInfoConstant.CUSTOMER_NAME)) {baseInfo.setCustomerName(str);DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");baseInfo.setQueryTime(LocalDateTime.parse(value, fmt));}if (map.containsValue(BaseInfoConstant.FUTURES_COMPANY_NAME)) {baseInfo.setFuturesCompanyName(str);baseInfo.setCusSecuritiesSpotInnnerFundAccount(value);return false;}return true;}
    /*** 处理期货期权账户出入金明细** @param map                   excel表格信息* @param optionAccountsDetails 账户出入金明细Excel集合* @return 是否期货期权账户出入金明细*/private Boolean dealOptionAccountsDetails(LinkedHashMap map, List<LinkedHashMap> optionAccountsDetails) {// 确定模块结束标识if (map.containsValue("合计")) {return false;}// 模块开始标识为模块标题以及模块第一行字段名,所以读取数据时要排除这两行if (!map.containsValue("发生日期") && !map.containsValue("期货期权账户出入金明细")) {optionAccountsDetails.add(map);}return true;}

PS:为什么确定模块开始标识和结束标识?
确定了开始和结束标识相当于直接将读取数据锁死,只读取开始和结束中间的数据,避免数据误读!

以上是Easy Excel读取复杂表格的代码分析,请多指教!
晚安,玛卡巴卡~

Easy Excel读取复杂表格文件相关推荐

  1. Python 从 Excel 读取链接下载文件

    Python 从 Excel 读取链接下载文件 import os from urllib.parse import urljoin, quote, unquoteimport requests im ...

  2. python读取wps表格文件显示到动态页面中_Python实现读取txt文件并转换为excel的方法示例...

    {"moduleinfo":{"card_count":[{"count_phone":1,"count":1}],&q ...

  3. easy excel生成简单表格

    主要的代码罗列如下: /*** 上传带数据的模板** @param headerList 模板表头* @param list 模板数据* @param process 进程* @param tenan ...

  4. Java生成Excel表格数据的两种方式(Easy Excel和SXSSFWorkbook)以及测试方法

    一. 使用alibaba中的Easy Excel工具 在gradle文件中引入依赖 dependencies {implementation 'com.alibaba:easyexcel:3.2.0' ...

  5. vue读取excel表格数据_vue 利用 js-xslx 读取 excel 表格文件

    文件读取~~~ 表格数据 excel读取数据.jpg // template // 文件选择,或者使用原生 imput type 为 file action="/" :on-cha ...

  6. python读取Excel表格文件

    python读取Excel表格文件,例如获取这个文件的数据 python读取Excel表格文件,需要如下步骤: 1.安装Excel读取数据的库-----xlrd 直接pip install xlrd安 ...

  7. 基于Python读取Excel表格文件数据并转换为字典dict格式

      有时我们需要将一个Excel表格文件中的全部或一部分数据导入到Python并转换为字典格式,如何实现呢?   我们以如下所示的一个表格(.xlsx格式)作为简单的示例.其中,表格共有两列,第一列为 ...

  8. 有选择读取word表格中的数据并写入excel文件中

    最近学院要举行科技报告会活动,许多同学积极参与,交了许多报名表到我这里,而我需要将这些信息进行汇总,整理出一个excel表格,看着一个个word文件放在我的工作文件夹中头发发麻,这一个个的整理好麻烦, ...

  9. Py自动化办公—Word文档替换、Excel表格读取、Pdf文件生成和Email自动邮件发送实战案例...

    点击上方"Python爬虫与数据挖掘",进行关注 回复"书籍"即可获赠Python从入门到进阶共10本电子书 今 日 鸡 汤 平阳歌舞新承宠,帘外春寒赐锦袍. ...

最新文章

  1. AngularJs学习笔记--Forms
  2. 用计算机计算线性卷积的基本规则,实验三_线性卷积与圆周卷积的计算.doc
  3. Linux 技术篇-查看cpu核心数,grep常用信息筛选语法使用演示
  4. OPNsense 18.7.X汉化包发布!
  5. 人类信息接收过去、现在与未来 ——我对于内容产业的判断
  6. solidity modifier函数修改器 智能合约开发知识浅学(三)
  7. flutter跨平台开发之App升级方案
  8. linux查看系统的版本信息失败,Linux - 查看系统的版本信息
  9. 2019 第二周 开发笔记
  10. python优先级队列-python 优先级队列
  11. raw socket编程实例
  12. 手势密码解锁微信小程序项目源码
  13. IDA遇到mojava,crash的情况
  14. 计算机导论黄国兴百度云,计算机导论黄国兴
  15. 常用的OpenCV函数速查
  16. Euclid算法和拓展欧几里得算法
  17. 006.集成Dva-core到react-native中
  18. 4800u功耗测试软件,ryzen controller---移动端锐龙处理器解锁功耗工具(附使用方法)...
  19. 习题3.6 阅读下面程序,分析其执行过程,写出输出结果
  20. 2022年个人融资方法和工具研究报告

热门文章

  1. ⌈ 2022杀青 ⌋ 一个普通人的年终总结,这一年我获得了更多的新鲜体验 | 普通而平凡的一年 | 向前走,走到光里 | 2023你好
  2. 会计面试经常会被提问的11个问题!!
  3. 计算机心理部的活动记录表,《计算机系12月心理活动策划书【荐】.doc》-支持高清全文免费浏览-max文档...
  4. Keil软件添加新源文件
  5. JavaScript--函数--参数--事件函数的关系--形参和实参
  6. 【04】Cockatrice界面构成
  7. 自动驾驶路径规划——DWA(动态窗口法)
  8. 如何搭建一个自己的网站-域名篇(上)
  9. 计算机专业必备电脑软件,你们的电脑桌面都有哪些必备的办公软件?
  10. 阳性感染者被要求带病上班?广东省人社厅:阳性感染者应居家隔离