EasyExcel工具读取Excel空数据行问题的解决办法

EasyExcel是Alibaba开源的一个Java处理Excel的工具。

官网解读:快速、简洁、解决大文件内存溢出的java处理Excel工具

快速

快速的读取excel中的数据。

简洁

映射excel和实体类,让代码变的更加简洁。

大文件

在读写大文件的时候使用磁盘做缓存,更加的节约内存。

官网地址:https://easyexcel.opensource.alibaba.com/

感兴趣可自己琢磨,该工具简单易上手,且性能相对比较高。

本文主要处理的问题是该工具读取Excel空数据行的问题。

首先解释为什么会产生空数据行:简单解释就是你在Excel中设置了单元的样式,却没有给单元格设值。因此,该工具在读取数据时便没有判断这一步,直接读取到整行数据均为null。

理解了核心问题后,要解决这个问题,实现思路也不难。

莫非就是把这种空数据行过滤即可。

本文是基于批处理监听器实现数据读取的,自定义集成该监听器(com.alibaba.excel.read.listener.PageReadListener),实现自己的逻辑即可解决问题。

下面是自定义监听器

package xin.cosmos.basic.easyexcel.framework;

import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.PageReadListener;
import com.alibaba.excel.util.ListUtils;
import lombok.extern.slf4j.Slf4j;
import xin.cosmos.basic.util.ObjectsUtil;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collectors;

/**
 * 自定义分批Excel数据读取监听器,解决官方无法移除空的Excel行问题
 *
 * @param <T>
 * @author geng
 */
@Slf4j
public class BatchPageReadListener<T> extends PageReadListener<T> {

    /**
     * Temporary storage of data
     */
    private List<T> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
    /**
     * consumer
     */
    private final Consumer<List<T>> consumer;

    public BatchPageReadListener(Consumer<List<T>> consumer) {
        super(consumer);
        this.consumer = consumer;
    }

    @Override
    public void invoke(T data, AnalysisContext context) {
        // 如果一行Excel数据均为空值,则不装载该行数据
        if (isLineNullValue(data)) {
            return;
        }
        cachedDataList.add(data);
        if (cachedDataList.size() >= BATCH_COUNT) {
            consumer.accept(cachedDataList);
            cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
        }
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        if (ObjectsUtil.isNull(cachedDataList)) {
            return;
        }
        consumer.accept(cachedDataList);
    }

    /**
     * 判断整行单元格数据是否均为空
     */
    private boolean isLineNullValue(T data) {
        if (data instanceof String) {
            return ObjectsUtil.isNull(data);
        }
        try {
            List<Field> fields = Arrays.stream(data.getClass().getDeclaredFields())
                    .filter(f -> f.isAnnotationPresent(ExcelProperty.class))
                    .collect(Collectors.toList());
            List<Boolean> lineNullList = new ArrayList<>(fields.size());
            for (Field field : fields) {
                field.setAccessible(true);
                Object value = field.get(data);
                if (ObjectsUtil.isNull(value)) {
                    lineNullList.add(Boolean.TRUE);
                } else {
                    lineNullList.add(Boolean.FALSE);
                }
            }
            return lineNullList.stream().allMatch(Boolean.TRUE::equals);
        } catch (Exception e) {
            log.error("读取数据行[{}]解析失败: {}", data, e.getMessage());
        }
        return true;
    }
}

下面是我对EasyExcel封装的工具

package xin.cosmos.basic.easyexcel.helper;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.EasyExcelFactory;
import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.read.listener.PageReadListener;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;
import com.alibaba.fastjson.JSON;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.multipart.MultipartFile;
import xin.cosmos.basic.define.ResultVO;
import xin.cosmos.basic.easyexcel.framework.BatchPageReadListener;
import xin.cosmos.basic.easyexcel.template.HeadVO;
import xin.cosmos.basic.exception.PlatformException;
import xin.cosmos.basic.util.BeanMapUtil;
import xin.cosmos.basic.util.ObjectsUtil;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.util.*;

/**
 * EasyExcel 帮助类
 */
@Slf4j
public class EasyExcelHelper {

    /**
     * 读取Excel文件
     *
     * @param stream      文件流
     * @param entityClass 读取转换的Java对象类型
     * @param <T>
     * @return
     */
    public static <T> List<T> doReadExcelData(InputStream stream, Class<T> entityClass) {
        List<T> data = new LinkedList<>();
        EasyExcelFactory.read(stream, entityClass, new PageReadListener<T>(data::addAll)).sheet().doRead();
        return data;
    }

    /**
     * 读取Excel文件
     *
     * @param stream      文件流
     * @param entityClass 读取转换的Java对象类型
     * @param comparator  排序比较器
     * @param <T>
     * @return
     */
    public static <T> List<T> doReadExcelData(InputStream stream, Class<T> entityClass, Comparator<T> comparator) {
        List<T> data = new LinkedList<>();
        EasyExcelFactory.read(stream, entityClass, new BatchPageReadListener<T>(list -> {
            if (comparator != null) {
                list.sort(comparator);
            }
            data.addAll(list);
        })).sheet().doRead();
        return data;
    }

    /**
     * 读取Excel文件
     *
     * @param file        MultipartFile文件
     * @param entityClass 读取转换的Java对象类型
     */
    @SneakyThrows
    public static <T> List<T> doReadExcelData(MultipartFile file, Class<T> entityClass) {
        return doReadExcelData(file.getInputStream(), entityClass);
    }

    /**
     * 读取Excel文件
     *
     * @param file        File文件
     * @param entityClass 读取转换的Java对象类型
     */
    @SneakyThrows
    public static <T> List<T> doReadExcelData(File file, Class<T> entityClass) {
        List<T> data = new LinkedList<>();
        EasyExcelFactory.read(file, entityClass, new BatchPageReadListener<T>(data::addAll)).sheet().doRead();
        return data;
    }

    /**
     * Excel数据浏览器下载
     *
     * @param pathName    下载文件的完整路径名称
     * @param data        需下载数据
     * @param entityClazz 下载数据类型模板
     */
    @SneakyThrows
    public static <T> void downloadExcel(String pathName, List<T> data, Class<T> entityClazz) {
        try {
            // 构建Excel表头及数据体
            ExcelWriterSheetBuilder builder = EasyExcel.write(pathName)
                    .autoCloseStream(true)
                    .sheet("sheet1");
            doWriteWithDynamicColumns(builder, entityClazz, data);
        } catch (Exception e) {
            log.error("写文件错误:{}", e.getMessage());
            throw new PlatformException("Excel下载数据错误");
        }
    }

    /**
     * Excel数据浏览器下载
     * <p>
     * 前端下载js代码示例:
     * <pre>
     * function downloadFile(bytes, fileName) {
     *    const blob = new Blob([bytes], {type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'});
     *    if (window.navigator.msSaveOrOpenBlob) { // 兼容IE10
     *          navigator.msSaveBlob(blob, fileName)
     *    } else {
     *         const url = window.URL.createObjectURL(blob);
     *         const a = document.createElement('a');
     *         a.href = url;
     *         a.download = fileName;
     *         a.click();
     *         window.URL.revokeObjectURL(url);
     *    }
     * }
     * </pre>
     *
     * @param excelFileName 下载文件名称
     * @param response      响应容器
     * @param data          需下载数据
     * @param entityClazz   下载数据Bean实体类型,苏醒必须使用注解<code>@ExcelProperty</code>中value指定写出列的表头吗,名称
     */
    public static <T> void downloadExcelToResponse(HttpServletResponse response, String excelFileName, List<T> data, Class<T> entityClazz) {
        if (ObjectsUtil.isNull(data)) {
            log.error("写文件错误:{}", "暂无可下载的数据");
            writeErrMsg(response, "暂无可下载的数据");
            return;
        }
        try {
            // 这里注意 有同学反应使用swagger 会导致各种问题,请直接用浏览器或者用postman
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setCharacterEncoding("utf-8");
            if (excelFileName.endsWith(".xlsx") || excelFileName.endsWith(".xls") ||
                    excelFileName.endsWith(".XLSX") || excelFileName.endsWith(".XLS")) {
                excelFileName = excelFileName.substring(0, excelFileName.lastIndexOf("."));
            }
            // 这里URLEncoder.encode可以防止中文乱码 当然和easy excel没有关系
            String urlFileName = URLEncoder.encode(excelFileName, "UTF-8").replaceAll("\\+", "%20");
            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + urlFileName + ".xlsx");
            response.setHeader("excel-file-name", urlFileName + ".xlsx");

            // 构建Excel表头及数据体
            ExcelWriterSheetBuilder builder = EasyExcel.write(response.getOutputStream())
                    .excelType(ExcelTypeEnum.XLSX)
                    .autoCloseStream(true)
                    .sheet("sheet1");
            doWriteWithDynamicColumns(builder, entityClazz, data);
        } catch (Exception e) {
            log.error("写文件错误:{}", e.getMessage());
            writeErrMsg(response, e.getMessage());
        }
    }

    /**
     * EasyExcel支持动态列写数据
     *
     * @param builder     指定输出方式和样式
     * @param entityClazz 实体的Class对象
     * @param data        Excel行数据
     */
    public static <T> void doWriteWithDynamicColumns(ExcelWriterSheetBuilder builder, Class<T> entityClazz, List<T> data) {
        List<HeadVO> customizeHeads = new ArrayList<>();
        Field[] fieldArray = entityClazz.getDeclaredFields();
        // 获取类的注解
        for (Field field : fieldArray) {
            // 忽略导出属性
            if (field.isAnnotationPresent(ExcelIgnore.class)) {
                continue;
            }
            if (field.isAnnotationPresent(ExcelProperty.class)) {
                ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
                List<String> head = Arrays.asList(excelProperty.value());
                int index = excelProperty.index();
                int order = excelProperty.order();
                HeadVO headVO = HeadVO.builder().headTitle(head).index(index).order(order).field(field.getName()).build();
                customizeHeads.add(headVO);
            }
        }

        // 表头排序
        Collections.sort(customizeHeads);

        // 处理表头
        List<List<String>> heads = new ArrayList<>();
        List<String> fields = new ArrayList<>();
        for (int i = 0; i <= customizeHeads.size() - 1; i++) {
            heads.add(customizeHeads.get(i).getHeadTitle());
            fields.add(customizeHeads.get(i).getField());
        }

        // 处理数据
        List<List<Object>> objs = new ArrayList<>();
        List<Map<String, ?>> maps = BeanMapUtil.beansToMaps(data);
        maps.forEach(map -> {
            List<Object> obj = new ArrayList<>();
            for (String field : fields) {
                obj.add(map.get(field));
            }
            objs.add(obj);
        });
        builder.head(heads).doWrite(objs);
    }

    @SneakyThrows
    private static void writeErrMsg(HttpServletResponse response, String errMsg) {
        // 重置response
        response.reset();
        response.setContentType("application/json");
        response.setCharacterEncoding("utf-8");
        response.getWriter().println(JSON.toJSONString(ResultVO.failed(errMsg)));
    }
}
package xin.cosmos.basic.easyexcel.template;

import lombok.Builder;
import lombok.Data;

import java.util.List;

/**
 * EasyExcel 表头信息VO类
 */
@Builder
@Data
public class HeadVO implements Comparable<HeadVO> {

    /**
     * Excel表头名称
     */
    private List<String> headTitle;
    /**
     * Excel表头名称映射的Java对象属性名称
     */
    private String field;
    /**
     * 主排序
     */
    private int index;
    /**
     * 次排序
     */
    private int order;

    /**
     * 升序排序
     * @param o
     * @return
     */
    @Override
    public int compareTo(HeadVO o) {
        if (this.index == o.getIndex()) {
            return this.order - o.getOrder();
        }
        return this.index - o.getIndex();
    }
}

最后是一个基于Spring cglib的Map<==>Java Bean之间的转换工具

package xin.cosmos.basic.util;

import org.springframework.cglib.beans.BeanMap;
import xin.cosmos.basic.exception.PlatformException;

import java.util.*;

/**
 * bean和map互转 工具类
 * <p>
 * 使用到spring的cglib
 */
public class BeanMapUtil {

    /**
     * 将Bean转为Map
     *
     * @param bean
     * @param <T>
     * @return
     */
    public static <T> Map<String, ?> beanToMap(T bean) {
        BeanMap beanMap = BeanMap.create(bean);
        Map<String, Object> map = new HashMap<>();
        beanMap.forEach((key, value) -> map.put(String.valueOf(key), value));
        return map;
    }

    /**
     * 将Map转为Bean
     *
     * @param map
     * @param beanClazz
     * @param <T>
     * @return
     */
    public static <T> T mapToBean(Map<String, ?> map, Class<T> beanClazz) {
        T bean;
        try {
            bean = beanClazz.newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
            throw new PlatformException("Map集合转换到Bean失败");
        }
        BeanMap beanMap = BeanMap.create(bean);
        beanMap.putAll(map);
        return bean;
    }

    /**
     * 将一组Beans转为List
     *
     * @param dataList
     * @param <T>
     * @return
     */
    public static <T> List<Map<String, ?>> beansToMaps(List<T> dataList) {
        List<Map<String, ?>> list = new ArrayList<>();
        if (ObjectsUtil.isNull(dataList)) {
            return Collections.emptyList();
        }
        Map<String, ?> map;
        T bean;
        for (T t : dataList) {
            bean = t;
            map = beanToMap(bean);
            list.add(map);
        }
        return list;
    }

    /**
     * 将一组Map转为一组Beans
     *
     * @param dataMaps
     * @param beanClazz
     * @param <T>
     * @return
     */
    public static <T> List<T> mapsToBeans(List<Map<String, ?>> dataMaps, Class<T> beanClazz) {
        List<T> list = new ArrayList<>();
        if (ObjectsUtil.isNull(dataMaps)) {
            return Collections.emptyList();
        }
        Map<String, ?> map;
        for (Map<String, ?> dataMap : dataMaps) {
            map = dataMap;
            T bean = mapToBean(map, beanClazz);
            list.add(bean);
        }
        return list;
    }
}
 

总结

到此这篇关于EasyExcel工具读取Excel空数据行问题解决的文章就介绍到这了,更多相关EasyExcel读取Excel空数据行内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 使用SpringBoot+EasyExcel+Vue实现excel表格的导入和导出详解

    目录 一.导入和导出 二.导出数据为excel实现过程 三.将excel中的数据导入到数据库中 一.导入和导出 导入:通过解析excel表格中的数据,然后将数据放到一个集合中,接着通过对持久层操作,将数据插入到数据库中,再加载一下页面,从而实现了数据的导入 导出:导出也是直接对数据库进行操作,获取数据库中所有的数据,将其存储在一个集中,接着使用查询出来的的数据生成一个excel表格 其中导入和导出的功能实现都是基于EasyExcel实现的 EasyExcel是阿里巴巴开源的一个基于Java的简单

  • 关于easyExcel中读取Excel表头的实例说明

    目录 前言 1 环境准备 1 添加pom 2 添加dto对象 3 准备一个控制器 4 准备一个监听类 2 单表头Excel 3 多表头Excel 4 总结 前言 在使用easyExcel读取文件时,对于Excel的表头,在解析读取时分成不同的状态,需要加以区分. 1 环境准备 准备一个可以正常访问的SpringBoot项目. 1 添加pom <!-- https://mvnrepository.com/artifact/com.alibaba/easyexcel --> <depende

  • Java中Easyexcel 实现批量插入图片功能

    目录 1 Maven依赖 2 PictureModel 3CustomPictureHandler 4 调试代码 5 调试结果 注: 注: 1 Maven依赖 <!--hutool工具包--> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.5.1</version> </de

  • 阿里的Easyexcel读取Excel文件的方法(最新版本)

    目录 优化 简单使用读取Excel,返回List集合 完整的Excel简单读取类和测试   本篇文章主要介绍一下使用阿里开源的Easyexcel工具处理读取excel文件,因为之前自己想在网上找一下这个简单的立即上手的博客,发现很多文章的教程都针对比较旧的版本的Easyexcel,没有使用新版本的方法,导致很多方法都标志过期了或者运行时报错,所以本篇博客主要是使用最新版的Easyexcel去读取excel文件,顺便说一下目前新版本的特性. 优化 目前读取excel文件不再需要指定ExcelTyp

  • Java 中EasyExcel的使用方式

    目录 背景 项目构建及依赖 创建实体类 生成Excel 解析Excel 其他相关特殊用法 自定义转换器 保留两位小数 排除指定Excel列 小结 背景 系统中经常要导出大量的数据,格式基本上都是Excel,然而每次导表都是对系统内存的一次挑战. 在Java领域,生成或解析Excel的框架比较有名的当属Apache的poi和jxl了.但使用它们,会面临着严重的内存损耗问题.如果系统的并发量还不行,一旦导出大量数据,便会出现JVM频繁full gc,甚至导致OOM. EasyExcel是阿里巴巴开源

  • EasyExcel工具读取Excel空数据行问题的解决办法

    EasyExcel是Alibaba开源的一个Java处理Excel的工具. 官网解读:快速.简洁.解决大文件内存溢出的java处理Excel工具 快速 快速的读取excel中的数据. 简洁 映射excel和实体类,让代码变的更加简洁. 大文件 在读写大文件的时候使用磁盘做缓存,更加的节约内存. 官网地址:https://easyexcel.opensource.alibaba.com/ 感兴趣可自己琢磨,该工具简单易上手,且性能相对比较高. 本文主要处理的问题是该工具读取Excel空数据行的问题

  • PHP读取mssql json数据中文乱码的解决办法

    PHP及网页使用UTF-8编码,数据库是sql server2008,使用默认编码(936,即GBK编码) 当读取数据库数据时,使用php自带的json_encode()返回到前端,结果中文不显示. 解决办法如下: 这样,sql server 2008中的中文就可以在网页正常显示了. 如果要将中文正常插入到sql server 2008中,还要加入一条代码:$query = iconv("utf-8", "gbk//ignore", $query);//为了解决中文

  • 利用Python第三方库xlrd读取Excel中数据实例代码

    目录 1. 安装 xlrd 库 2. 使用 xlrd 库 2.1 打开 Excel 工作表对象 2.2 读取单个单元格数据 2.3 读取多个单元格数据 2.3 读取所有单元格数据 附:行.列操作 3. 总结 1. 安装 xlrd 库 Python 读取 Excel 中的数据主要用到 xlrd 第三方库.xlrd 其实就是两个单词的简化拼接,我们可以把它拆开来看,xl 代表 excel, rd 代表 read, 合并起来就是 xlrd, 意思就是读 excel 的第三方库. 这种命名风格也正是我们

  • 利用PHPExcel读取Excel的数据和导出数据到Excel

    PHPExcel是一个PHP类库,用来帮助我们简单.高效实现从Excel读取Excel的数据和导出数据到Excel.也是我们日常开发中,经常会遇到的使用场景.比如有个客户信息表,要批量导出发给同事,我们就可以用PHPExcel来快速实现.同样,如果我们要利用短信群发接口去群发信息,PHPExcel可以快速导入客户信息,避免人工录入信息的麻烦. PHPExcel使用教程: 首先下载PHPExcel 到https://github.com/PHPOffice/PHPExcel下载PHPExcel,如

  • python在CMD界面读取excel所有数据的示例

    代码 import xlrd import os from prettytable import PrettyTable import pandas #创建一个Excel表类 class Excel(object): def __init__(self, path): self.path = path //路径要加上文件名 #读取Excel内全部数据 参数sname是sheet页名字 def read_all_data(self, sname): workbook = xlrd.open_wor

  • Python Pandas读取Excel日期数据的异常处理方法

    目录 异常描述 出现原因 解决方案:修改自定义格式 pandas直接解析Excel数值为日期 总结 异常描述 有时我们的Excel有一个调整过自定义格式的日期字段: 当我们用pandas读取时却是这样的效果: 不管如何指定参数都无效. 出现原因 没有使用系统内置的日期单元格格式,自定义格式没有对负数格式进行定义,pandas读取时无法识别出是日期格式,而是读取出单元格实际存储的数值. 解决方案:修改自定义格式 可以修改为系统内置的自定义格式: 或者在自定义格式上补充负数的定义: 增加;@即可 p

  • 后端接收不到AngularJs中$http.post发送的数据原因分析及解决办法

    1.问题: 后端接收不到AngularJs中$http.post发送的数据,总是显示为null 示例代码: $http.post(/admin/KeyValue/GetListByPage, { pageindex: 1, pagesize: 8 }) .success(function(){ alert("Mr靖"); }); 代码没有错,但是在后台却接收不到数据,这是为什么呢? 用火狐监控:参数是JSON格式 用谷歌监控:传参方式是request payload 可以发现传参方式是

  • Ubuntu 18.04中截图工具shutter的编辑按钮不可用的解决办法

    Shutter是一个由第三方提供的在Ubuntu上运行的截图工具,相对于系统自带的截图工具(默认可通过Ctrl + Shift + Print快捷键启动截图),最大的优点就是可以即时对图片进行编辑,在图片上做一些标记和文字标注等,使用起来很方便.在Ubuntu 16.04上,该软件运行一切正常,当将操作系统升级到18.04之后,启动Shutter后你会发现原先的编辑按钮不可用了.点击编辑按钮,提示说缺少libgoo-canvas-perl库.看来我们不得不手动将该软件所依赖的库装一遍了. 按照以

  • ASP.NET中上传并读取Excel文件数据示例

    在CSDN中,经常有人问如何打开Excel数据库文件.本文通过一个简单的例子,实现读取Excel数据文件. 首先,创建一个Web应用程序项目,在Web页中添加一个DataGrid控件.一个文件控件和一个按钮控件. 复制代码 代码如下: <INPUT id="File1" type="file" name="File1" runat="server"> <asp:Button id="Button1&

随机推荐