Execl数据导入 EasyExcel实现

news2025/3/18 21:30:04

官网

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为忽略注解
    @ExcelIgnore
    private String elementStr;
    
    /**行号*/
    @ExcelIgnore
    private 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
             */
            @Override
            public void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {
                planInfoList.add(headMap.get(0).getStringValue());
            }

            /**
             * 读取非抬头行数据
             * @param data 行数据格式,自定义,默认为Object
             * @param analysisContext
             */
            @Override
            public void invoke(Summary data, AnalysisContext analysisContext) {
                if (StrUtil.isEmptyIfStr(data.getTotalSection())) {
                    data.setTotalSection(data.getSubSection());
                }
                saveList.add(data);
            }

            /**
             *
             * @param extra
             * @param context
             */
            @Override
            public 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
             */
            @Override
            public 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;
    }
}


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1789556.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

微信小程序公众号二合一分销商城源码系统 基于PHP+MySQL组合开发的 可多商户商家入驻 带完整的安装代码包以及搭建教程

系统概述 微信小程序公众号二合一分销商城源码系统&#xff0c;是基于PHPMySQL组合开发的一款高效、稳定的电子商务平台解决方案。该系统创新性地将微信公众号与小程序的功能进行了深度整合&#xff0c;为商家提供了一个功能齐全、易于管理的分销商城系统。通过此系统&#xf…

JavaSE——学习总结

一、初识Java 运行Java程序 Java是一门半编译型、半解释型语言 先通过javac编译程序把源文件进行编译&#xff0c;编译后生成的.class文件是由字节码组成的&#xff0c;和平台无关、面向JVM的文件&#xff0c;最后启动java虚拟机来运行.class文件&#xff0c;此时JVM会将字节…

Python | 刷题日记

1.海伦公式求三角形的面积 area根号下&#xff08;p(p-a)(p-b&#xff09;(p-c)) p是周长的一半 2.随机生成一个整数 import random xrandom.randint(0,9)#随机生成0到9之间的一个数 yeval(input("please input:")) if xy:print("bingo") elif x<y:pri…

(十五)统计学基础练习题九(选择题T401-450)

本文整理了统计学基础知识相关的练习题&#xff0c;共50道&#xff0c;适用于想巩固统计学基础或备考的同学。来源&#xff1a;如荷学数据科学题库&#xff08;技术专项-统计学三&#xff09;。序号之前的题请看往期文章。 401&#xff09; 402&#xff09; 403&#xff09; 4…

算法-随机快排及荷兰国旗优化

文章目录 算法介绍 :1. 随机快排解析2. 荷兰国旗问题3. 随机快排优化4. 总结随机快排 算法介绍 : 随机快速排序和传统的快速排序的逻辑本质是一致的,都是找到一个值作为划分的中间位置,左边数值均小于该数值,右边数值均大于该数值,但是与传统的快排又不一致的是,我们的这个位置…

15 - 有趣的电影(高频 SQL 50 题基础版)

15 - 有趣的电影 select* from cinema wheredescription!boring and id%2!0 order by rating desc;

黑龙江等保测评有哪些内容?

与等保1.0相比&#xff0c;新的等保2.0版本有了很大的变化&#xff0c;评估的内容、评估的标准也有了很大的差异。那么新版的《等保2.0》实施后&#xff0c;我们要测试什么&#xff1f; 等保测评首先是有十个大项&#xff0c;安全物理环境&#xff0c;安全区域边界&#xff0c;…

标准发布 | 反渗透和纳滤水处理膜修复再利用技术指南

一、编制单位 本文件由浙江大学、中华环保联合会水环境治理专业委员会提出。 本文件由中华环保联合会归口。 本文件主编单位&#xff1a;浙江大学、河南一膜环保技术有限公司、安徽精高水处理有限公司、国能龙源环保有限公司、湖南沁森高科新材料有限公司。 本文件参编单位&…

getway整合sentinel流控降级

3. 启动sentinel控制台增加流控规则&#xff1a; 根据API分组进行流控&#xff1a; 1.设置API分组&#xff1a; 2.根据API分组进行流控&#xff1a; 自定义统一异常处理&#xff1a; nginx负载配置&#xff1a;

Redis位图

简介 在我们平时开发过程中&#xff0c;会有一些bool型数据需要存取&#xff0c;比如用户一年的签到记录&#xff0c;签了是1&#xff0c;没签是0&#xff0c;要记录365天。如果使用普通的key/value&#xff0c;每个用户要记录365个&#xff0c;当用户上亿的时候&#xff0c;需…

湖南(选址调研)源点咨询 商铺开业前选址调研重要性与流程解析

湖南长沙&#xff08;市场定位&#xff09;源点市场调研认为&#xff0c;选址前首要的准备工作就是对店铺地址进行周密的调查&#xff0c;列出一份详尽的选址调查报告&#xff0c;从而逐一分析店铺的选址的利与弊&#xff0c;最后确定该地址是否适合店铺的运营。源点调研通过多…

电商API商品数据采集接口||助力电商企业采集商品大数据提高开发效率

提高开发效率&#xff1a;电商API接口允许不同的应用程序之间高效地进行交互&#xff0c;节省了大量的人力物力成本&#xff0c;使得开发者可以将更多时间和精力集中于自身的核心业务。 增加数据安全性&#xff1a;通过对数据进行安全加密&#xff0c;API接口实现了对数据的保护…

硬件26、EDA绘制板框

1、放置-板框-矩形 2、在pcb上绘制出需要大小的板框 3、设置板框四个角为圆弧状&#xff0c;在右侧属性栏设置圆角半径

系统工程与信息系统基础

三、企业信息化 目的&#xff1a;提高企业的竞争力 信息化需求&#xff1a; 战略需求&#xff1a;提升组织的竞争能力 运作需求&#xff1a;实现信息化战略目标、运作策略、人才培养的需要 技术需求&#xff1a;信息技术层面上对系统的完善、升级、集成 企业信息化方法&…

ArcGIS for Vue3

二维&#xff1a; 1、创建vue项目 npm create vitelatest 2、安装ArcGIS JS API依赖包 npm install arcgis/core 3、引入ArcGIS API for JavaScript模块 <script setup> import "arcgis/core/assets/esri/themes/light/main.css"; import Map from arcgis…

功效系数法

功效系数法&#xff08;Efficacy Coefficient Method&#xff09;是一种综合评价方法&#xff0c;它根据多目标规划的原理&#xff0c;对每个评价指标确定一个满意值和不允许值&#xff0c;以满意值为上限&#xff0c;以不允许值为下限。计算各指标实现满意值的程度&#xff0c…

Gorm中time.time的默认时区

问题复现 期望查询结果时区使用本机的默认时区&#xff08;东八区&#xff09;&#xff1a; 2024-06-05 04:43:54 0800 CST 实际查询结果&#xff08;UTC&#xff09;&#xff1a; 2024-06-04 20:43:54 0000 UTC 如何解决 问题是通过gorm查询mysql数据库时做了转化&#xff0…

目标检测数据集 - 城市道路行驶车辆检测数据集下载「包含VOC、COCO、YOLO三种格式」

​​​数据集介绍&#xff1a;城市道路行驶车辆检测数据集&#xff0c;真实监控场景高质量图片数据&#xff0c;涉及场景丰富&#xff0c;比如城市道路快速行驶车辆、城市道路慢速行驶车辆、城市道路密集行驶车辆、城市道路夜间低光行驶车辆数据等。数据集标注标签划分为 "…

信息可溯、安全可控 | SW-LIMS 采测分离监测模式解析

数据的准确性在环境监测过程中至关重要,为了确保环监数据的真实有效,并满足“全程留痕、全程监控、信息可溯、安全可控”的要求,采测分离监测模式是一个有效的解决方案。 这种模式通过将样品采集和样品检测交由不同的单位完成,形成了相互独立、相互监督的工作机制,有助于减少潜…

七.传输层协议——再谈UDP协议

一.传输层协议地位 在上一节中&#xff0c;我们提到到了HTTP,HTTPS等应用层协议&#xff0c;现在我们就要进一步向下挖掘&#xff0c;来到我们的传输层协议 假如说应用层负责传递的是数据的内容&#xff0c;专注于为⽤户提供应⽤功能&#xff1b;那传输层协议负责的就是传输的…