easyExcel Java 操作遇到的部分问题记录

1.  问题:You can define up to 64000 style in a .xlsx Workbook  

工具类中:

/**
 * 部分列设置为text 格式的导出
 *
 * @param excelWriter
 * @param iterablePagedSupplier
 * @param dataType
 * @param sheetNo
 * @param sheetName
 * @param <E>
 */
public <E> void repeatedWriteWithText(ExcelWriter excelWriter, IterablePagedSupplier<E> iterablePagedSupplier,
    Class<E> dataType, Integer sheetNo, String sheetName, List<Integer> needSetTextColumns) {
    //自己定义了一个handler TextCellHandler 用于处理部分列设置为文本格式导出,防止数字过长出现科学计数法 然后编辑时丢失精度的问题
    WriteSheet writeSheet = EasyExcel.writerSheet(sheetNo, sheetName)
        .head(dataType)
        .registerWriteHandler(new TextCellHandler(needSetTextColumns)) 
        .build();

    while (iterablePagedSupplier.hasNextPage()) {
        List<E> records = iterablePagedSupplier.getPage();
        excelWriter.write(records, writeSheet);
    }
}

TextCellHandler类:

package com.**.**.module.common.excel;

import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.*;

import java.util.List;

/**
 * @version 1.0
 * @ClassName SomeTextCellHandler
 * @Description
 * @Author coral
 * @Date 2021/3/2
 * @changeList
 */
public class TextCellHandler implements CellWriteHandler {

    /**
     * 需要设置成text格式的列
     */
    private List<Integer> needSetTextColums;
    private Workbook workbook;
    private CellStyle cellStyle;
    private DataFormat dataFormat;

    public TextCellHandler(List<Integer> needSetTextColums) {
        this.needSetTextColums = needSetTextColums;
    }

    @Override
    public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row,
        Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) {
    }

    @Override
    public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell,
        Head head, Integer relativeRowIndex, Boolean isHead) {
        if (workbook == null) {
            workbook = writeSheetHolder.getSheet()
                .getWorkbook();
            cellStyle = workbook.createCellStyle();
            dataFormat = workbook.createDataFormat();
            cellStyle.setDataFormat(dataFormat.getFormat("@"));
        }
        if (needSetTextColums.contains(cell.getColumnIndex())) {
            //把需要设置为文本格式的单元格变成文本
            cell.setCellStyle(cellStyle);
        }
    }

    @Override
    public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder,
        List<CellData> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
    }
}

红色部分代码很关键 ,如果我们在下面的if判断里面 每一个单元格绘制完成都去设置对应的格式,一旦导出行太多,也就是需要设置的单元格超过了64000

则会出现You can define up to 64000 style in a .xlsx Workbook    ,因为easyExcel是循环写每一列,

创建了很多的 cellstyle, 在easyExcel中会抛出错误  。 解决方案是只创建一个cellStyle ,然后对应的cell设置格式即可。

2.问题:Excel导入我们往往需要校验,校验完了再去执行后续的保存等操作,例如我们要校验导入的Excel表头对不对,客户瞎导入是不行的,这样我们只想获取一行,就是表头这一行,其他的不用载读取。 通常一个Excel行数有时候会很多,例如10万行,甚至50万行。 我们需要的是校验一旦发现数据不对,就能中断后续的继续读取或者校验,

中断Excel的后续校验方法如下,:

第一种,直接抛异常,、

  多数情况下这种方式是有问题的,还需要我们catch异常,给出合理的提示,或者说是异常出现之后的需要再执行就没办法了,这种方法基本不会采用。

第二种,找到中断方法

例如: 获取表头,获取完成之后就不在读取之后的行 节省大量的时间 和无用功
/**
 * 获取表头
 *
 * @param file             文件
 * @param headDataListener 处理表头数据的listener
 * @param <E>
 */
public <E> void getHeadMap(MultipartFile file, HeadDataListener<E> headDataListener) {
    try {
        EasyExcel.read(file.getInputStream(), headDataListener)
            .sheet(0)
            .doRead();
    } catch (Exception e) {
        e.printStackTrace();
        log.error("parse excel error, file:{}", file.getName(), e);
        throw new BusinessException(ERROR_EXCEL_IMPORT_VALIDATE.getMsg(), ERROR_EXCEL_IMPORT_VALIDATE.getCode());
    }
}

自定义的headDataListener类

直接重载 AnalysisEventListener hasNext 的方法, 这个方法是判断是否继续往下读取行的方法可以从easyExcel源码中看到。
package com.**.**.module.common.excel;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.read.listener.ReadListener;
import com.*.web.exception.BusinessException;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.extern.slf4j.Slf4j;

import java.util.List;
import java.util.Map;


/**
 * @version 1.0
 * @ClassName HeadDataListener
 * @Description 只用来读取表头的数据
 * @Author 河山
 * @Date 2021/1/31
 * @changeList
 */
@EqualsAndHashCode(callSuper = true)
@Data
@Slf4j
public class HeadDataListener<T> extends AnalysisEventListener<T> {
    /**
     * 表头
     */
    private Map<Integer, String> headMap;

    /**
     * 能够支持的一个excle最大的行数
     */
    private int approximateTotalRowNumber;

    private int processedRows = 0;

    @Override
    public void invoke(T t, AnalysisContext analysisContext) {
        processedRows++;
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {

    }

    @Override
    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
        this.headMap = headMap;
        Integer approximateTotalRowNumber = context.readSheetHolder()
            .getApproximateTotalRowNumber();
        this.approximateTotalRowNumber = approximateTotalRowNumber;
    }

    @Override
    public boolean hasNext(AnalysisContext analysisContext) {
        if(processedRows>=1){
            return false;
        }
        return true;
    }
}

源码在easyExcel Java 操作遇到的部分问题记录

 

 

上一篇:Java(18):easyExcel如何使用实体映射关系直接读写Excel数据


下一篇:EasyExcel 的使用