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为忽略注解
    @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;
    }
}


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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

移动端 UI 风格,视觉盛宴

移动端 UI 风格&#xff0c;视觉盛宴

10.dockerfile自动构建镜像

dockerfile自动构建镜像 类似ansible剧本&#xff0c;大小几kb 手动做镜像&#xff1a;大小几百M 首先创建一个dockerfile的路径&#xff0c;便于在路径下存在多个路径每个路径下都是dockerfile命名的脚本 注释&#xff1a;文件必须为&#xff1a;dockerfile或者Dockerfile …

QT中为程序加入超级管理员权限

QT中为程序加入超级管理员权限 Chapter1 QT中为程序加入超级管理员权限1. mingw编译器2. MSVC编译器3. CMAKE Chapter2 如何给QT程序添加管理员权限(UAC)的几种方法1、Qt Creator中方案一&#xff1a;&#xff08;仅适用于使用msvc编译器&#xff09;方案二&#xff1a;&#x…

单链表复习 (C语言版)

目录 一.顺序表与链表的区别 二.链表概念 三.单链表 1.单链表的开始与初始化 2.单链表的打印 3.单链表的尾插 重难点&#xff1a;单链表实现时的指针详解 4.单链表的头插 5.单链表的尾删 6.单链表的头删 小结&#xff1a; 7.单链表的查找 8.在指定位置前插入数据 …

制作AI问答机器人:从0到1的完整指南

在数字化转型的浪潮中&#xff0c;企业正追求更高效、智能的客户服务解决方案。AI问答机器人以其快速响应、全天候服务和持续学习的能力&#xff0c;成为了提升客户满意度和加速业务发展的关键工具。本文将深入探讨如何制作一个企业级的AI问答机器人&#xff0c;并强调其功能体…

【Linux】(五)—— SSH远程登录和XShell使用

SSH Linux中的SSH&#xff08;Secure Shell&#xff09;是一个强大的网络协议&#xff0c;用于在不安全的网络环境中提供安全的远程登录和资料拷贝等其他网络服务。以下是有关Linux中SSH的关键点和操作指南&#xff1a; SSH的基础概念 安全性&#xff1a;SSH通过对所有传输的…

编程规范-代码检测-格式化-规范化提交

适用于vue项目的编程规范 – 在多人开发时统一编程规范至关重要 1、代码检测 --Eslint Eslint&#xff1a;一个插件化的 javascript 代码检测工具 在 .eslintrc.js 文件中进行配置 // ESLint 配置文件遵循 commonJS 的导出规则&#xff0c;所导出的对象就是 ESLint 的配置对…

【Pytorch】一文向您详细介绍 torch.Tensor() 的常见用法

【Pytorch】一文向您详细介绍 torch.Tensor() 的常见用法 下滑即可查看博客内容 &#x1f308; 欢迎莅临我的个人主页 &#x1f448;这里是我静心耕耘深度学习领域、真诚分享知识与智慧的小天地&#xff01;&#x1f387; &#x1f393; 博主简介&#xff1a;985高校的普通…

WebAPI AOP方式 异常方式 FilterAttribute、ActionFilterAttribute

》》 自定义异常处理特性 using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using System.Web; using System.Web.Http.Filters;namespace WebApplication11 {/// <summary>/// 异常处理特性/// </sum…

Transformer动画讲解:注意力计算Q、K、V

暑期实习基本结束了&#xff0c;校招即将开启。 不同以往的是&#xff0c;当前职场环境已不再是那个双向奔赴时代了。求职者在变多&#xff0c;HC 在变少&#xff0c;岗位要求还更高了。提前准备才是完全之策。 最近&#xff0c;我们又陆续整理了很多大厂的面试题&#xff0c…

九种mfc140u.dll丢失的解决方法,全面解决mfc140u.dll文件丢失

mfc140u.dll是 Microsoft Visual C 2015 Redistributable 的一部分&#xff0c;它与 Microsoft 基础类库&#xff08;MFC&#xff09;的 Unicode 版本有关。当您在运行使用 Visual C 2015 开发的应用程序时&#xff0c;可能会碰到关于mfc140u.dll丢失的错误。下面列出了一些解决…

WPF前端:一个纯Xaml的水平导航栏

效果图&#xff1a; 代码&#xff1a; 1、样式代码&#xff0c;可以写在窗体资源处或者样式资源文件中 <Style x:Key"MenuRadioButtonStyle" TargetType"{x:Type RadioButton}"><Setter Property"FontSize" Value"16" />…

Zabbix实现邮件和钉钉实时告警(使用python脚本)

告警和通知 告警是监控的主要职能,是指将到达某一阈值事件的消息发送给用户,让用户在事件发生的时候即刻知道监控项处于不正常状态,从而让用户来决定是否采取相关措施。 zabbix中,告警是由一系列的流程组成的,⾸首先是触发器到达阈值,接下是Active对事件信息进行处理,其…

谁能赢?阿里的通义 VS 百度的文心

关注卢松松&#xff0c;会经常给你分享一些我的经验和观点。 国产AI大模型领域&#xff0c;当前有两大阵营&#xff1a; (1)以百度文心一言为代表的闭源大模型。李彦宏曾说过&#xff1a;AI大模型开源意义不大&#xff0c;百度绝不抢开发者饭碗。 (2)以阿里通义AI为代表的开…

个股场外期权个人如何参与买卖?

个股场外期权作为一种金融衍生品&#xff0c;为个人投资者提供了多样化的投资选择和风险管理工具。想要参与个股场外期权的买卖&#xff0c;以下是一些关键步骤和考虑因素。 文章来源/&#xff1a;财智财经 第一步&#xff1a;选择合适的金融机构 首先&#xff0c;个人投资者需…

Android 常用开源库 MMKV 源码分析与理解

文章目录 前言一、MMKV简介1.mmap2.protobuf 二、MMKV 源码详解1.MMKV初始化2.MMKV对象获取3.文件摘要的映射4.loadFromFile 从文件加载数据5.数据写入6.内存重整7.数据读取8.数据删除9.文件回写10.Protobuf 实现1.序列化2.反序列化 12.文件锁1.加锁2.解锁 13.状态同步 总结参考…

17- Redis 中的 quicklist 数据结构

在 Redis 3.0 之前&#xff0c;List 对象的底层数据结构是双向链表或者压缩列表&#xff0c;然后在 Redis 3.2 的时候&#xff0c;List 对象的底层改由 quicklist 数据结构实现。 其实 quicklist 就是【双向链表 压缩列表】组合&#xff0c;因为一个 quicklist 就是一个链表&…

视频修复工具,模糊视频变清晰!

老旧视频画面效果差&#xff0c;视频效果模糊。我们经常找不到一个好的工具来让视频更清晰&#xff0c;并把它变成高清画质。相信很多网友都会有这个需求&#xff0c;尤其是视频剪辑行业的网友&#xff0c;经常会遇到这个问题。今天给大家分享一个可以把模糊视频修复清晰的工具…

【ffmpeg】本地格式转换 mp4转wav||裁剪mp4

个人感受&#xff1a;太爽了&#xff01;&#xff01;&#xff01;&#xff08;可能用惯了转换网站和无良的转换软件&#xff09; ———— 使用FFmpeg把mp4文件转换为WAV文件 - 简书 (jianshu.com) FFMPEG 视频分割和合并 - 简书 (jianshu.com) ———— 示例 ffmpeg -i …

C# MemoryCache 缓存应用

摘要 缓存是一种非常常见的性能优化技术&#xff0c;在开发过程中经常会用到。.NET提供了内置的内存缓存类 MemoryCache&#xff0c;它可以很方便地存储数据并在后续的请求中快速读取&#xff0c;从而提高应用程序的响应速度。 正文 通过使用 Microsoft.Extensions.Caching.Me…