easyExcel使用總結
注意:本文講述的是當easyExcel官網提供的api無法滿足我們的開發要求時,如單元格合併(假設第一列合併、第二列合併、第一列和第二列合併)、對某個單元格根據來動態着色等。此時實現我們一些需要的功能時,就需要使實現一些hendler來實現我們的需求。
1、提供的擴展點
workbook創建前後,所有操作完成後過程中提供了改writehandler
public interface WorkbookWriteHandler extends WriteHandler {
void beforeWorkbookCreate();
void afterWorkbookCreate(WriteWorkbookHolder writeWorkbookHolder);
void afterWorkbookDispose(WriteWorkbookHolder writeWorkbookHolder)
}
sheet創建前後有SheetWriteHandler
public interface SheetWriteHandler extends WriteHandler {
void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder);
void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder);
}
Row創建前後有RowWriteHandler
public interface RowWriteHandler extends WriteHandler {
void beforeRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Integer rowIndex,
Integer relativeRowIndex, Boolean isHead);
void afterRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row,
Integer relativeRowIndex, Boolean isHead);
void afterRowDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row,
Integer relativeRowIndex, Boolean isHead);
}
Cell操作過程有CellWriteHandler:
public interface CellWriteHandler extends WriteHandler {
void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head,
Integer columnIndex, Integer relativeRowIndex, Boolean isHead);
void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head,
Integer relativeRowIndex, Boolean isHead);
void afterCellDataConverted(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, CellData cellData,
Cell cell, Head head, Integer relativeRowIndex, Boolean isHead);
void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder,
List<CellData> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead);
}
2、如何註冊handler
EasyExcel.writerSheet(0)
.registerWriteHandler(new xxxHnadler()).build()
3、大致思路
public File generatorExcel(BusinessQuery query) {
BusinessSummary businessSummary = this.getBusinessSummary(query);
try {
Excels excels = new Excels(tempate.xlsx");
SheetInfo sheet1 = new SheetInfo(1);
//合併規則
MergeCellInfo groupCellMerge = new MergeCellInfo(0, 0,new MergeCellInfo(1,1)); //先按第一個單元格合併
ArrayList<MergeCellInfo> mergeCellInfos = Lists.newArrayList(groupCellMerge,new MergeCellInfo(0, 1));
MergeRowWriteHandler sheet1Merge = new MergeRowWriteHandler(mergeCellInfos);
sheet1.setWriteHandlers(Lists.newArrayList(sheet1Merge));
sheet1.setFillData(this.fillListToMap(fillTypeMap));
sheet1.setDataList(this.toExcelListData(TemplateTypeEnum.JD,businessTrendAlls, BusinessTrendAll.class));
excels.writeSheet(sheet1);
log.info("sheet2處理完成.....耗時:{} ms",stopwatch.stop().elapsed(TimeUnit.MILLISECONDS));
stopwatch.reset().start();
File file = excels.toFile();
return file;
} catch (Exception e) {
log.error("generatorExcel生成excel失敗",e);
throw new RuntimeException(e);
}
public void writeSheet(SheetInfo sheetInfo) throws Exception {
try {
//校驗參數
this.validateSheetInfo(sheetInfo);
WriteSheet writeSheet = null;
if ((writeSheet = this.writeSheetMap.get(sheetInfo.getSheetIndex())) == null) {
ExcelWriterSheetBuilder excelWriterSheetBuilder = EasyExcel.writerSheet();
//從小到大排序
TreeSet<AbstractRowAndCellWriteHandler> handlers = new TreeSet<AbstractRowAndCellWriteHandler>(Comparator.naturalOrder());
handlers.add(new DefaultCellValueWriteHandler());
if (sheetInfo.getWriteHandlers() != null) {
handlers.addAll(sheetInfo.getWriteHandlers());
}
handlers.stream().filter(Objects::nonNull).forEach(excelWriterSheetBuilder::registerWriteHandler);
writeSheet = excelWriterSheetBuilder.sheetName(sheetInfo.getSheetName()).sheetNo(sheetInfo.getSheetIndex()).build();
this.writeSheetMap.put(sheetInfo.getSheetIndex(), writeSheet);
}
//填充單元格
if (sheetInfo.getFillData() != null) {
excelWriter.fill(sheetInfo.getFillData(), writeSheet);
}
//寫單元格
List<List<EnhanceCellData>> lists = this.convertDataList(sheetInfo.getDataList());
this.getCustomerHandler(writeSheet).forEach(el -> el.beforeWrite(lists));
if (!CollectionUtils.isEmpty(lists)) {
excelWriter.write(lists, writeSheet);
}
//清理
this.getCustomerHandler(writeSheet).forEach(el -> el.afterWrite());
} catch(Exception e) {
excelWriter.finish();
}
}
public class MergeRowWriteHandler extends AbstractRowAndCellWriteHandler {
//表頭所佔用的行數
Integer headRelativeLastRowIndex = null;
List<MergeCellInfo> mergeCellInfos = null;
//行對應的合併信息 存放的是 合併截至行 index, key:表示開始要合併的行 相對數據的index,不是sheet的row index, value:具體要合併的的行和列信息,一行可能有多個需要合併的列
Map<Integer, List<CellRangeAddress>> rowMergeInfo = new HashMap<>();
public MergeRowWriteHandler(List<MergeCellInfo> mergeCellInfos) {
this.mergeCellInfos = mergeCellInfos;
}
@Override
public void beforeWrite(List<List<EnhanceCellData>> dataLists) {
super.beforeWrite(dataLists);
if (CollectionUtils.isEmpty(dataLists) || CollectionUtils.isEmpty(mergeCellInfos)) {
return;
}
//在寫入之前,動態計算如何合併
for (MergeCellInfo mergeCellInfo : mergeCellInfos) {
computerMergeInfo(mergeCellInfo,dataLists);
}
}
private void computerMergeInfo(MergeCellInfo mergeCellInfo,List<List<EnhanceCellData>> rootDataList){
//獲取要一行中要合併單元格之間的列數據(就是把要把某一行要合併的某幾列數找到,放到list中)
Function<List<EnhanceCellData>,List<SheetCellData>> cellDataRange = (List<EnhanceCellData> rowData)-> rowData.stream()
.skip(mergeCellInfo.getStartCellIndex())
.limit(mergeCellInfo.getEndCellIndex() - mergeCellInfo.getStartCellIndex() + 1)
.map(EnhanceCellData::getSheetCellData).collect(Collectors.toList());
//按列進行分組(把相同的放在連續的編號)
Map<String, List<List<EnhanceCellData>>> startIndexGroup = rootDataList.stream().filter(rowData -> {
//要合併的單元格之間的數據
List<SheetCellData> cellRangeData = cellDataRange.apply(rowData);
//一行裏面需要合併的單元格範圍中任何一個單元格爲空就不進行分組了,並且橫向合併的數據必須一樣,不然就不需要合併了
return cellRangeData.stream().allMatch(el->el!=null && el.getFieldValue()!=null)
&& cellRangeData.stream().map(SheetCellData::getFieldValue).distinct().count()==1;
}).collect(Collectors.groupingBy(rowData -> {
//要合併的單元格之間的數據
List<String> cellRangeDataValues = cellDataRange.apply(rowData).stream().map(el->el.getFieldValue().toString()).collect(Collectors.toList());
return String.join("", cellRangeDataValues);
}));
for (Map.Entry<String, List<List<EnhanceCellData>>> entry : startIndexGroup.entrySet()) {
List<List<EnhanceCellData>> lists = entry.getValue();
Integer minRelativeRowIndex = lists.stream().flatMap(el -> el.stream()).map(EnhanceCellData::getRelativeListDataRowIndex).min(Integer::compareTo).get();
Integer maxRelativeRowIndex = lists.stream().flatMap(el -> el.stream()).map(EnhanceCellData::getRelativeListDataRowIndex).max(Integer::compareTo).get();
//同一行且是同一列 就不做合併
if (minRelativeRowIndex - maxRelativeRowIndex == 0 && mergeCellInfo.getEndCellIndex() - mergeCellInfo.getStartCellIndex() == 0) {
continue;
}
//號都是從0開始的
CellRangeAddress cellRangeAddress = new CellRangeAddress(minRelativeRowIndex, maxRelativeRowIndex, mergeCellInfo.getStartCellIndex(), mergeCellInfo.getEndCellIndex());
// System.out.println("合併單元格======>"+maxRowIndex+","+maxRowIndex+","+mergeCellInfo.getStartCellIndex()+","+mergeCellInfo.getEndCellIndex());
List<CellRangeAddress> mergeRows= null;
if((mergeRows = rowMergeInfo.get(minRelativeRowIndex)) ==null){
mergeRows = Lists.newArrayList();
rowMergeInfo.put(minRelativeRowIndex, mergeRows);
}else{
}
//找出在本次合併規則中的重複合併,並刪除
List<CellRangeAddress> alreadyIncludeRule = mergeRows.stream()
.filter(el -> cellRangeAddress.getFirstColumn() <= el.getFirstColumn() && el.getLastColumn() <= cellRangeAddress.getLastColumn())
.collect(Collectors.toList());
alreadyIncludeRule.forEach(mergeRows::remove); //刪除能覆蓋的規則
mergeRows.add(cellRangeAddress);
//處理子任務合併
if(mergeCellInfo.getSubMergeCellInfo()!=null){
computerMergeInfo(mergeCellInfo.getSubMergeCellInfo(),lists);
}
}
}
@Override
public void afterRowDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row,
Integer relativeRowIndex, Boolean isHead) {
if (isHead || relativeRowIndex == null) {
return;
}
if(this.headRelativeLastRowIndex ==null){
//定位到表頭的行數,不包含當前行
this.headRelativeLastRowIndex = writeSheetHolder.getNewRowIndexAndStartDoWrite()-1;
}
//相對數據中的行
List<CellRangeAddress> cellRangeAddressList = rowMergeInfo.remove(relativeRowIndex);
if (cellRangeAddressList == null) {
return;
}
//在行銷燬的時候進行合併
for (CellRangeAddress el : cellRangeAddressList) {
el.setFirstRow(headRelativeLastRowIndex + el.getFirstRow());
el.setLastRow(headRelativeLastRowIndex + el.getLastRow());
writeSheetHolder.getSheet().addMergedRegionUnsafe(el);
// //不要調用代碼來設置居中,採用根據模版來設置居中
// writeSheetHolder.getSheet().setVerticallyCenter(false);
// writeSheetHolder.getSheet().setHorizontallyCenter(false);
}
}
@Override
public void afterWrite() {
super.afterWrite();
this.rowMergeInfo =null;
this.mergeCellInfos =null;
this.headRelativeLastRowIndex =null;
}
}
public abstract class AbstractRowAndCellWriteHandler implements CellWriteHandler, RowWriteHandler, Ordered,Comparable<AbstractRowAndCellWriteHandler> {
protected List<List<EnhanceCellData>> dataLists;
@Override
public int compareTo(AbstractRowAndCellWriteHandler handler) {
return handler.getOrder();
}
@Override
public int getOrder() {
return 0;
}
@Override
public void beforeRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Integer rowIndex,
Integer relativeRowIndex, Boolean isHead) {
}
@Override
public void afterRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row,
Integer relativeRowIndex, Boolean isHead) {
}
@Override
public void afterRowDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row,
Integer relativeRowIndex, Boolean isHead) {
}
@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) {
}
@Override
public void afterCellDataConverted(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder,
CellData cellData, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
}
@Override
public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder,
List<CellData> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
}
//開始執行write方法的時候
public void beforeWrite(List<List<EnhanceCellData>> dataLists){
this.dataLists = dataLists;
}
//渲染方法完成
public void afterWrite(){
this. dataLists = null;
}
}
public class DefaultCellValueWriteHandler extends AbstractRowAndCellWriteHandler {
private Map<String,XSSFCellStyle> styleMap = new HashMap<>();
@Override
public int getOrder() {
return Integer.MAX_VALUE;
}
@Override
public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder,
List<CellData> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
if(cell==null){
return ;
}
if (isHead) {
return;
}
if(CollectionUtils.isEmpty(cellDataList) || !(cellDataList.get(0) instanceof EnhanceCellData)){
return;
}
try {
EnhanceCellData enhanceCellData =(EnhanceCellData) cellDataList.get(0);
SheetCellData sheetCellData = enhanceCellData.getSheetCellData();
//Converter<Object> converter = DefaultConverterLoader.loadDefaultWriteConverter().get(ConverterKeyBuild.buildKey(enhanceCellData.getFieldType()));
if(sheetCellData!=null){
if (Date.class.isAssignableFrom(sheetCellData.getFieldType())) {
cell.setCellValue((Date) sheetCellData.getFieldValue());
}else if (String.class.isAssignableFrom(sheetCellData.getFieldType())) {
cell.setCellValue(sheetCellData.getFieldValue().toString());
cell.setCellType(CellType.STRING);
}else if (Boolean.class.isAssignableFrom(sheetCellData.getFieldType())) {
cell.setCellValue(Boolean.valueOf(sheetCellData.getFieldValue().toString()));
cell.setCellType(CellType.BOOLEAN);
}else if (Number.class.isAssignableFrom(sheetCellData.getFieldType())) {
cell.setCellValue((new BigDecimal(sheetCellData.getFieldValue().toString())).doubleValue());
cell.setCellType(CellType.NUMERIC);
}else{
cell.setCellValue(sheetCellData.getFieldValue().toString());
}
if(sheetCellData!=null && sheetCellData.getWrapText()!=null){
cell.getCellStyle().setWrapText(sheetCellData.getWrapText());
}
}
cell.setCellStyle(this.getCellStyle((XSSFWorkbook)writeSheetHolder.getParentWriteWorkbookHolder().getWorkbook(), enhanceCellData));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private CellStyle getCellStyle(XSSFWorkbook workbook, EnhanceCellData enhanceCellData){
XSSFCellStyle cellStyle=null;
String key = this.getKey(enhanceCellData);
if((cellStyle=styleMap.get(key))==null){
cellStyle = workbook.createCellStyle();
SheetCellData sheetCellData = enhanceCellData.getSheetCellData();
if(sheetCellData!=null){
if(StringUtils.hasText(sheetCellData.getDataFormat())){
DataFormat format = workbook.createDataFormat();
cellStyle.setDataFormat(format.getFormat(sheetCellData.getDataFormat()));
}
if(sheetCellData.getWrapText()!=null){
cellStyle.setWrapText(sheetCellData.getWrapText());
}
if(sheetCellData.getBgColor()!=null){
cellStyle.setFillForegroundColor(sheetCellData.getBgColor().getIndex());
cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
}
}
//其他默認的樣式,不要讓其自動居中
if(sheetCellData!=null){
if(sheetCellData.getLeftAlign()!=null){
if(sheetCellData.getLeftAlign())
cellStyle.setAlignment(HorizontalAlignment.LEFT);
else
cellStyle.setAlignment(HorizontalAlignment.CENTER);
}else {
cellStyle.setAlignment(HorizontalAlignment.CENTER);
}
}
cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
cellStyle.setBorderBottom(BorderStyle.THIN);
cellStyle.setBorderTop(BorderStyle.THIN);
cellStyle.setBorderLeft(BorderStyle.THIN);
cellStyle.setBorderRight(BorderStyle.THIN);
styleMap.put(key,cellStyle);
}
return cellStyle;
}
/**
* 根據單元格對象生成單元格樣式,同樣的樣式只創建一遍
* @param enhanceCellData
* @return
*/
private String getKey(EnhanceCellData enhanceCellData){
SheetCellData sheetCellData = enhanceCellData.getSheetCellData();
if(enhanceCellData==null || sheetCellData ==null){
return null;
}
StringBuilder keyBuilder = new StringBuilder();
Optional.ofNullable(sheetCellData.getDataFormat()).filter(StringUtils::hasLength).ifPresent(keyBuilder::append);
Optional.ofNullable(sheetCellData.getBgColor()).ifPresent(keyBuilder::append);
Optional.ofNullable(sheetCellData.getWrapText()).ifPresent(keyBuilder::append);
return keyBuilder.length()==0?null:keyBuilder.toString();
}
}
public class MergeRowWriteHandler extends AbstractRowAndCellWriteHandler {
//表頭所佔用的行數
Integer headRelativeLastRowIndex = null;
List<MergeCellInfo> mergeCellInfos = null;
//行對應的合併信息 存放的是 合併截至行 index, key:表示開始要合併的行 相對數據的index,不是sheet的row index, value:具體要合併的的行和列信息,一行可能有多個需要合併的列
Map<Integer, List<CellRangeAddress>> rowMergeInfo = new HashMap<>();
public MergeRowWriteHandler(List<MergeCellInfo> mergeCellInfos) {
this.mergeCellInfos = mergeCellInfos;
}
@Override
public void beforeWrite(List<List<EnhanceCellData>> dataLists) {
super.beforeWrite(dataLists);
if (CollectionUtils.isEmpty(dataLists) || CollectionUtils.isEmpty(mergeCellInfos)) {
return;
}
//在寫入之前,動態計算如何合併
for (MergeCellInfo mergeCellInfo : mergeCellInfos) {
computerMergeInfo(mergeCellInfo,dataLists);
}
}
private void computerMergeInfo(MergeCellInfo mergeCellInfo,List<List<EnhanceCellData>> rootDataList){
//獲取要一行中要合併單元格之間的列數據(就是把要把某一行要合併的某幾列數找到,放到list中)
Function<List<EnhanceCellData>,List<SheetCellData>> cellDataRange = (List<EnhanceCellData> rowData)-> rowData.stream()
.skip(mergeCellInfo.getStartCellIndex())
.limit(mergeCellInfo.getEndCellIndex() - mergeCellInfo.getStartCellIndex() + 1)
.map(EnhanceCellData::getSheetCellData).collect(Collectors.toList());
//按列進行分組(把相同的放在連續的編號)
Map<String, List<List<EnhanceCellData>>> startIndexGroup = rootDataList.stream().filter(rowData -> {
//要合併的單元格之間的數據
List<SheetCellData> cellRangeData = cellDataRange.apply(rowData);
//一行裏面需要合併的單元格範圍中任何一個單元格爲空就不進行分組了,並且橫向合併的數據必須一樣,不然就不需要合併了
return cellRangeData.stream().allMatch(el->el!=null && el.getFieldValue()!=null)
&& cellRangeData.stream().map(SheetCellData::getFieldValue).distinct().count()==1;
}).collect(Collectors.groupingBy(rowData -> {
//要合併的單元格之間的數據
List<String> cellRangeDataValues = cellDataRange.apply(rowData).stream().map(el->el.getFieldValue().toString()).collect(Collectors.toList());
return String.join("", cellRangeDataValues);
}));
for (Map.Entry<String, List<List<EnhanceCellData>>> entry : startIndexGroup.entrySet()) {
List<List<EnhanceCellData>> lists = entry.getValue();
Integer minRelativeRowIndex = lists.stream().flatMap(el -> el.stream()).map(EnhanceCellData::getRelativeListDataRowIndex).min(Integer::compareTo).get();
Integer maxRelativeRowIndex = lists.stream().flatMap(el -> el.stream()).map(EnhanceCellData::getRelativeListDataRowIndex).max(Integer::compareTo).get();
//同一行且是同一列 就不做合併
if (minRelativeRowIndex - maxRelativeRowIndex == 0 && mergeCellInfo.getEndCellIndex() - mergeCellInfo.getStartCellIndex() == 0) {
continue;
}
//號都是從0開始的
CellRangeAddress cellRangeAddress = new CellRangeAddress(minRelativeRowIndex, maxRelativeRowIndex, mergeCellInfo.getStartCellIndex(), mergeCellInfo.getEndCellIndex());
// System.out.println("合併單元格======>"+maxRowIndex+","+maxRowIndex+","+mergeCellInfo.getStartCellIndex()+","+mergeCellInfo.getEndCellIndex());
List<CellRangeAddress> mergeRows= null;
if((mergeRows = rowMergeInfo.get(minRelativeRowIndex)) ==null){
mergeRows = Lists.newArrayList();
rowMergeInfo.put(minRelativeRowIndex, mergeRows);
}else{
}
//找出在本次合併規則中的重複合併,並刪除
List<CellRangeAddress> alreadyIncludeRule = mergeRows.stream()
.filter(el -> cellRangeAddress.getFirstColumn() <= el.getFirstColumn() && el.getLastColumn() <= cellRangeAddress.getLastColumn())
.collect(Collectors.toList());
alreadyIncludeRule.forEach(mergeRows::remove); //刪除能覆蓋的規則
mergeRows.add(cellRangeAddress);
//處理子任務合併
if(mergeCellInfo.getSubMergeCellInfo()!=null){
computerMergeInfo(mergeCellInfo.getSubMergeCellInfo(),lists);
}
}
}
@Override
public void afterRowDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row,
Integer relativeRowIndex, Boolean isHead) {
if (isHead || relativeRowIndex == null) {
return;
}
if(this.headRelativeLastRowIndex ==null){
//定位到表頭的行數,不包含當前行
this.headRelativeLastRowIndex = writeSheetHolder.getNewRowIndexAndStartDoWrite()-1;
}
//相對數據中的行
List<CellRangeAddress> cellRangeAddressList = rowMergeInfo.remove(relativeRowIndex);
if (cellRangeAddressList == null) {
return;
}
//在行銷燬的時候進行合併
for (CellRangeAddress el : cellRangeAddressList) {
el.setFirstRow(headRelativeLastRowIndex + el.getFirstRow());
el.setLastRow(headRelativeLastRowIndex + el.getLastRow());
writeSheetHolder.getSheet().addMergedRegionUnsafe(el);
// //不要調用代碼來設置居中,採用根據模版來設置居中 // writeSheetHolder.getSheet().setVerticallyCenter(false); // writeSheetHolder.getSheet().setHorizontallyCenter(false); } }
@Override
public void afterWrite() {
super.afterWrite();
this.rowMergeInfo =null;
this.mergeCellInfos =null;
this.headRelativeLastRowIndex =null;
}
}