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;
}
}
源码在