当前位置: 首页 > news >正文

Execl数据导入 EasyExcel实现

官网

1. 需求简介

读取下面表格数据
在这里插入图片描述
第一行和第二行是计划信息
第三行是计划详情的抬头信息,以下行是计划详情信息
总段包含多个分段,总段使用了单元格合并功能

2. 实现读取功能

2.1 引入easyexcel依赖

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

2.2 创建计划详情行信息对象

package com.gkdz.server.modules.shipyard.domain;import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;import java.util.Date;@Getter
@Setter
@EqualsAndHashCode
public class Summary {@ExcelProperty(value = "序号", index = 0)private String number;@ExcelProperty(value = "总段", index = 1)private String totalSection;@ExcelProperty(value = "分段", index = 2)private String subSection;@ExcelProperty(value = "分段预估重量(T)", index = 3)private Double subExpectWeight;@ExcelProperty(value = "长", index = 4)private Double length;@ExcelProperty(value = "宽", index = 5)private Double width;@ExcelProperty(value = "高", index = 6)private Double height;@ExcelProperty(value = "分段类型", index = 7)private String type;@ExcelProperty(value = "总段预估重量(T)", index = 8)private Double totalExpectWeight;@ExcelProperty(value = "分段交付时间", index = 9)private String subSectionDeliveryTime;@ExcelProperty(value = "总组吊装日期", index = 10)private String hoistDate;@ExcelProperty(value = "总组焊前", index = 11)private String totalBeWeldDate;@ExcelProperty(value = "总组焊前周期", index = 12)private String totalBeWeldCycle;@ExcelProperty(value = "总组完工日期", index = 13)private String totalCompletionDate;@ExcelProperty(value = "总组完工周期", index = 14)private String totalCompletionCycle;@ExcelProperty(value = "搭载吊装日期", index = 15)private String carryLiftDate;@ExcelProperty(value = "搭载焊前日期", index = 16)private String carryBeWeldDate;@ExcelProperty(value = "搭载焊前周期", index = 17)private String carryBeWeldCycle;@ExcelProperty(value = "搭载完工日期", index = 18)private String carryCompletionDate;@ExcelProperty(value = "搭载完工周期", index = 19)private String carryCompletionCycle;@ExcelProperty(value = "吊装顺序", index = 20)private String hoistOrder;@ExcelProperty(value = "封舱设备", index = 21)private String sealingEquipment;@ExcelProperty(value = "备注", index = 22)private String remark;//标记,用于分组,ExcelIgnore为忽略注解@ExcelIgnoreprivate String elementStr;/**行号*/@ExcelIgnoreprivate int rowNo;
}

2.3 读取execl工具类

package com.gkdz.server.modules.shipyard.util;import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.enums.CellExtraTypeEnum;
import com.alibaba.excel.metadata.CellExtra;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.fastjson.JSON;
import com.gkdz.server.modules.shipyard.domain.Summary;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.multipart.MultipartFile;import java.io.IOException;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.*;@Slf4j
public class EasyExcelUtil {/*** 计划详情正文起始行*/private static final Integer headRowNumber = 3;/*** 分组切割符号*/public static final String splitFlag = "<zld>";public static Map<String, List> uploadByFile(MultipartFile file) throws IOException {Map<String, List> map = new HashMap<>();//正文行数据List<Summary> saveList = new ArrayList<>();//抬头行数据List<String> planInfoList = new ArrayList<>();//合并单元格数据List<CellExtra> extraMergeInfoList = new ArrayList<>();EasyExcel.read(file.getInputStream(), Summary.class, new ReadListener<Summary>() {/*** 读取表格抬头行数据,默认为读第一行;* 可设置headRowNumber(headRowNumber):抬头行为前headRowNumber行* @param headMap* @param context*/@Overridepublic void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {planInfoList.add(headMap.get(0).getStringValue());}/*** 读取非抬头行数据* @param data 行数据格式,自定义,默认为Object* @param analysisContext*/@Overridepublic void invoke(Summary data, AnalysisContext analysisContext) {if (StrUtil.isEmptyIfStr(data.getTotalSection())) {data.setTotalSection(data.getSubSection());}saveList.add(data);}/**** @param extra* @param context*/@Overridepublic void extra(CellExtra extra, AnalysisContext context) {log.info("读取到了一条额外信息:{}", JSON.toJSONString(extra));switch (extra.getType()) {case COMMENT: {log.info("额外信息是批注,在rowIndex:{},columnIndex;{},内容是:{}", extra.getRowIndex(), extra.getColumnIndex(),extra.getText());break;}case HYPERLINK: {if ("Sheet1!A1".equals(extra.getText())) {log.info("额外信息是超链接,在rowIndex:{},columnIndex;{},内容是:{}", extra.getRowIndex(),extra.getColumnIndex(), extra.getText());} else if ("Sheet2!A1".equals(extra.getText())) {log.info("额外信息是超链接,而且覆盖了一个区间,在firstRowIndex:{},firstColumnIndex;{},lastRowIndex:{},lastColumnIndex:{},"+ "内容是:{}",extra.getFirstRowIndex(), extra.getFirstColumnIndex(), extra.getLastRowIndex(),extra.getLastColumnIndex(), extra.getText());} else {log.error("Unknown hyperlink!");}break;}case MERGE: {if (extra.getRowIndex() >= headRowNumber) {extraMergeInfoList.add(extra);}break;}default: {}}}/*** 数据读取完毕后执行的方法,可做数据整体处理* @param analysisContext*/@Overridepublic void doAfterAllAnalysed(AnalysisContext analysisContext) {}}).extraRead(CellExtraTypeEnum.MERGE).sheet().headRowNumber(headRowNumber).doRead();final List<Summary> summaries = ExcelSplitUtil.explainMergeData(saveList, extraMergeInfoList, headRowNumber);map.put("planInfo", planInfoList);map.put("summary", summaries);return map;}/*** 根据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);}return results;}
}

2.4 拆分单元格工具

package com.gkdz.server.modules.shipyard.util;import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.metadata.CellExtra;
import com.gkdz.server.modules.shipyard.domain.Summary;
import lombok.extern.slf4j.Slf4j;import java.lang.reflect.Field;
import java.util.List;/*** @description:拆分单元格数据*/
@Slf4j
public class ExcelSplitUtil {/*** 处理合并单元格** @param data               解析数据* @param extraMergeInfoList 合并单元格信息* @param headRowNumber      起始行* @return 填充好的解析数据*/public static List<Summary> explainMergeData(List<Summary> data, List<CellExtra> extraMergeInfoList, Integer headRowNumber) {
//        循环所有合并单元格信息extraMergeInfoList.forEach(cellExtra -> {int firstRowIndex = cellExtra.getFirstRowIndex() - headRowNumber;int lastRowIndex = cellExtra.getLastRowIndex() - headRowNumber;int firstColumnIndex = cellExtra.getFirstColumnIndex();int lastColumnIndex = cellExtra.getLastColumnIndex();
//            获取初始值Object initValue = getInitValueFromList(firstRowIndex, firstColumnIndex, data);
//            设置值for (int i = firstRowIndex; i <= lastRowIndex; i++) {for (int j = firstColumnIndex; j <= lastColumnIndex; j++) {setInitValueToList(initValue, i, j, data);}}});return data;}/*** 设置合并单元格的值** @param filedValue  值* @param rowIndex    行* @param columnIndex 列* @param data        解析数据*/private static void setInitValueToList(Object filedValue, Integer rowIndex, Integer columnIndex, List<Summary> data) {Summary object = data.get(rowIndex);for (Field field : object.getClass().getDeclaredFields()) {//提升反射性能,关闭安全检查field.setAccessible(true);ExcelProperty annotation = field.getAnnotation(ExcelProperty.class);if (annotation != null) {if (annotation.index() == columnIndex) {try {field.set(object, filedValue);break;} catch (IllegalAccessException e) {log.error("解析数据时发生异常!");}}}}}/*** 获取合并单元格的初始值* rowIndex对应list的索引* columnIndex对应实体内的字段** @param firstRowIndex    起始行* @param firstColumnIndex 起始列* @param data             列数据* @return 初始值*/private static Object getInitValueFromList(Integer firstRowIndex, Integer firstColumnIndex, List<Summary> data) {Object filedValue = null;Summary object = data.get(firstRowIndex);for (Field field : object.getClass().getDeclaredFields()) {//提升反射性能,关闭安全检查field.setAccessible(true);ExcelProperty annotation = field.getAnnotation(ExcelProperty.class);if (annotation != null) {if (annotation.index() == firstColumnIndex) {try {filedValue = field.get(object);break;} catch (IllegalAccessException e) {log.error("解析数据时发生异常!");}}}}return filedValue;}
}

相关文章:

  • PICRUSt2在微生物功能预测分析中的应用解读
  • 【区分vue2和vue3下的element UI TimePicker 时间选择器组件,分别详细介绍属性,事件,方法如何使用,并举例】
  • Qt+qss动态属性改变控件状态切换的样式
  • Flutter打包网络问题解决办法
  • 【Go语言精进之路】构建高效Go程序:了解切片实现原理并高效使用
  • 《编译原理》期末考试复习手写笔记(二)+真题(第四、五、六章)+课后习题答案
  • Flutter中同步与异步
  • 10倍速开发开关电源:PSIM DLL集成指南与如何单步调试你的代码
  • 【面试干货】如何选择MySQL数据库存储引擎(MyISAM 或 InnoDB)
  • 使用 Python 的 Tkinter 来创建 GUI 应用程序
  • 【排序算法】总结篇
  • Linux-常用命令-常用设置
  • 测试testing06081
  • 华为端云一体化开发 初始化云db表结构和表数据(实践2.0)(HarmonyOS学习第七课)
  • Electron qt开发教程
  • 分享一款快速APP功能测试工具
  • 「面试题」如何实现一个圣杯布局?
  • iOS帅气加载动画、通知视图、红包助手、引导页、导航栏、朋友圈、小游戏等效果源码...
  • Java IO学习笔记一
  • JS函数式编程 数组部分风格 ES6版
  • Js基础知识(一) - 变量
  • MD5加密原理解析及OC版原理实现
  • PAT A1092
  • Protobuf3语言指南
  • Python3爬取英雄联盟英雄皮肤大图
  • SOFAMosn配置模型
  • Spring Cloud中负载均衡器概览
  • vue从创建到完整的饿了么(18)购物车详细信息的展示与删除
  • webpack入门学习手记(二)
  • 个人博客开发系列:评论功能之GitHub账号OAuth授权
  • 基于MaxCompute打造轻盈的人人车移动端数据平台
  • 免费小说阅读小程序
  • 如何编写一个可升级的智能合约
  • 删除表内多余的重复数据
  • 使用阿里云发布分布式网站,开发时候应该注意什么?
  • 提醒我喝水chrome插件开发指南
  • 无服务器化是企业 IT 架构的未来吗?
  • 没有任何编程基础可以直接学习python语言吗?学会后能够做什么? ...
  • 数据库巡检项
  • #QT(一种朴素的计算器实现方法)
  • #图像处理
  • (html5)在移动端input输入搜索项后 输入法下面为什么不想百度那样出现前往? 而我的出现的是换行...
  • (pt可视化)利用torch的make_grid进行张量可视化
  • (编译到47%失败)to be deleted
  • (代码示例)使用setTimeout来延迟加载JS脚本文件
  • (附源码)计算机毕业设计SSM疫情居家隔离服务系统
  • (区间dp) (经典例题) 石子合并
  • (三)Kafka 监控之 Streams 监控(Streams Monitoring)和其他
  • (淘宝无限适配)手机端rem布局详解(转载非原创)
  • (五) 一起学 Unix 环境高级编程 (APUE) 之 进程环境
  • (原創) 如何安裝Linux版本的Quartus II? (SOC) (Quartus II) (Linux) (RedHat) (VirtualBox)
  • .NET Reactor简单使用教程
  • .net 程序发生了一个不可捕获的异常
  • .net反混淆脱壳工具de4dot的使用
  • .NET教程 - 字符串 编码 正则表达式(String Encoding Regular Express)