最近在研究EasyPoi的excel导出功能,发现其不支持表头各个单元格样式自定义,所以就对其导出功能进行了重写。
一、EasyPoi源码分析
在进行重写之前,首先要了解EasyPoi源码中,对表头处理的代码具体是如何实现的。查看源码可以看出,导出功能主要是通过ExcelExportService类来实现,而在其中可以看到向excel中填入表头及数据的方法为insertDataToSheet,具体代码如下:
protected void insertDataToSheet(Workbook workbook, ExportParams entity,
List<ExcelExportEntity> entityList, Collection<?> dataSet,
Sheet sheet) {
try {
dataHandler = entity.getDataHandler();
if (dataHandler != null && dataHandler.getNeedHandlerFields() != null) {
needHandlerList = Arrays.asList(dataHandler.getNeedHandlerFields());
}
dictHandler = entity.getDictHandler();
commentHandler = entity.getCommentHandler();
// 创建表格样式
setExcelExportStyler((IExcelExportStyler) entity.getStyle()
.getConstructor(Workbook.class).newInstance(workbook));
Drawing patriarch = PoiExcelGraphDataUtil.getDrawingPatriarch(sheet);
List<ExcelExportEntity> excelParams = new ArrayList<ExcelExportEntity>();
if (entity.isAddIndex()) {
excelParams.add(indexExcelEntity(entity));
}
excelParams.addAll(entityList);
sortAllParams(excelParams);
int index = entity.isCreateHeadRows()
? createHeaderAndTitle(entity, sheet, workbook, excelParams) : 0;
int titleHeight = index;
setCellWith(excelParams, sheet);
setColumnHidden(excelParams, sheet);
short rowHeight = entity.getHeight() != 0 ? entity.getHeight() : getRowHeight(excelParams);
setCurrentIndex(1);
createAddressList(sheet, index, excelParams, 0);
Iterator<?> its = dataSet.iterator();
List<Object> tempList = new ArrayList<Object>();
while (its.hasNext()) {
Object t = its.next();
index += createCells(patriarch, index, t, excelParams, sheet, workbook, rowHeight, 0)[0];
tempList.add(t);
if (index >= MAX_NUM) {
break;
}
}
if (entity.getFreezeCol() != 0) {
sheet.createFreezePane(entity.getFreezeCol(), 0, entity.getFreezeCol(), 0);
}
mergeCells(sheet, excelParams, titleHeight);
its = dataSet.iterator();
for (int i = 0, le = tempList.size(); i < le; i++) {
its.next();
its.remove();
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("List data more than max ,data size is {}",
dataSet.size());
}
// 发现还有剩余list 继续循环创建Sheet
if (dataSet.size() > 0) {
createSheetForMap(workbook, entity, entityList, dataSet);
} else {
// 创建合计信息
addStatisticsRow(getExcelExportStyler().getStyles(true, null), sheet);
}
} catch (Exception e) {
LOGGER.error(e.getMessage(), e);
throw new ExcelExportException(ExcelExportEnum.EXPORT_ERROR, e);
}
}
而在此方法中进一步分析,可看出表头数据及样式操作的方法为createHeaderAndTitle,跳转后可确定操作表头方法为createHeaderRow,具体代码如下:
private int createHeaderRow(ExportParams title, Sheet sheet, Workbook workbook, int index,
List<ExcelExportEntity> excelParams, int cellIndex) {
Row row = sheet.getRow(index) == null ? sheet.createRow(index) : sheet.getRow(index);
int rows = getRowNums(excelParams, true);
row.setHeight(title.getHeaderHeight());
Row listRow = null;
if (rows >= 2) {
listRow = sheet.getRow(index + 1);
if (listRow == null) {
listRow = sheet.createRow(index + 1);
listRow.setHeight(title.getHeaderHeight());
}
}
int groupCellLength = 0;
CellStyle titleStyle = getExcelExportStyler().getTitleStyle(title.getColor());
for (int i = 0, exportFieldTitleSize = excelParams.size(); i < exportFieldTitleSize; i++) {
ExcelExportEntity entity = excelParams.get(i);
// 加入换了groupName或者结束就,就把之前的那个换行
if (StringUtils.isBlank(entity.getGroupName()) || i == 0 || !entity.getGroupName().equals(excelParams.get(i - 1).getGroupName())) {
if (groupCellLength > 1) {
sheet.addMergedRegion(new CellRangeAddress(index, index, cellIndex - groupCellLength, cellIndex - 1));
}
groupCellLength = 0;
}
if (StringUtils.isNotBlank(entity.getGroupName())) {
createStringCell(row, cellIndex, entity.getGroupName(), titleStyle, entity);
createStringCell(listRow, cellIndex, entity.getName(), titleStyle, entity);
groupCellLength++;
} else if (StringUtils.isNotBlank(entity.getName())) {
createStringCell(row, cellIndex, entity.getName(), titleStyle, entity);
}
if (entity.getList() != null) {
// 保持原来的
int tempCellIndex = cellIndex;
cellIndex = createHeaderRow(title, sheet, workbook, rows == 1 ? index : index + 1, entity.getList(), cellIndex);
List<ExcelExportEntity> sTitel = entity.getList();
if (StringUtils.isNotBlank(entity.getName()) && sTitel.size() > 1) {
PoiMergeCellUtil.addMergedRegion(sheet, index, index, tempCellIndex, tempCellIndex + getFieldLength(sTitel));
}
/*for (int j = 0, size = sTitel.size(); j < size; j++) {
createStringCell(rows == 2 ? listRow : row, cellIndex, sTitel.get(j).getName(),
titleStyle, entity);
cellIndex++;
}*/
cellIndex--;
} else if (rows > 1 && StringUtils.isBlank(entity.getGroupName())) {
createStringCell(listRow, cellIndex, "", titleStyle, entity);
PoiMergeCellUtil.addMergedRegion(sheet, index, index + rows - 1, cellIndex, cellIndex);
}
cellIndex++;
}
if (groupCellLength > 1) {
PoiMergeCellUtil.addMergedRegion(sheet, index, index, cellIndex - groupCellLength, cellIndex - 1);
}
return cellIndex;
}
分析源码可看出,表头的样式主要是根据excelParams参数中存储的样式来设定,而此参数对应的类型(ExcelExportEntity)中,样式字段只有宽度和高度,而这就是EasyPoi不支持自定义表头样式的原因。
通过上述分析,我们可知,若要支持自定义表头样式,我们首先考虑在createHeaderRow方法的中增加表头样式的参数(List<ExcelExportExtendEntity> extendList),而此方法为private方法,所以只能在继承ExcelExportService类的自定义类中重载此方法。继而,需要依次重载createHeaderAndTitle、insertDataToSheet、createSheetForMap。而之所以要依次重载这些方法,主要原因在于,表头自定义样式的获取最好和本身excelParams参数内容的获取放在一起,防止重复调用。而excelParams参数内容是通过getAllExcelField方法来获取,源码如下:
public void getAllExcelField(String[] exclusions, String targetId, Field[] fields,
List<ExcelExportEntity> excelParams, Class<?> pojoClass,
List<Method> getMethods, ExcelEntity excelGroup) throws Exception {
List<String> exclusionsList = exclusions != null ? Arrays.asList(exclusions) : null;
ExcelExportEntity excelEntity;
// 遍历整个filed
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
// 先判断是不是collection,在判断是不是java自带对象,之后就是我们自己的对象了
if (PoiPublicUtil.isNotUserExcelUserThis(exclusionsList, field, targetId)) {
continue;
}
// 首先判断Excel 可能一下特殊数据用户回自定义处理
if (field.getAnnotation(Excel.class) != null) {
Excel excel = field.getAnnotation(Excel.class);
String name = PoiPublicUtil.getValueByTargetId(excel.name(), targetId, null);
if (StringUtils.isNotBlank(name)) {
excelParams.add(createExcelExportEntity(field, targetId, pojoClass, getMethods, excelGroup));
}
} else if (PoiPublicUtil.isCollection(field.getType())) {
ExcelCollection excel = field.getAnnotation(ExcelCollection.class);
ParameterizedType pt = (ParameterizedType) field.getGenericType();
Class<?> clz = (Class<?>) pt.getActualTypeArguments()[0];
List<ExcelExportEntity> list = new ArrayList<ExcelExportEntity>();
getAllExcelField(exclusions,
StringUtils.isNotEmpty(excel.id()) ? excel.id() : targetId,
PoiPublicUtil.getClassFields(clz), list, clz, null, null);
excelEntity = new ExcelExportEntity();
excelEntity.setName(PoiPublicUtil.getValueByTargetId(excel.name(), targetId, null));
if (i18nHandler != null) {
excelEntity.setName(i18nHandler.getLocaleName(excelEntity.getName()));
}
excelEntity.setOrderNum(Integer
.valueOf(PoiPublicUtil.getValueByTargetId(excel.orderNum(), targetId, "0")));
excelEntity
.setMethod(PoiReflectorUtil.fromCache(pojoClass).getGetMethod(field.getName()));
excelEntity.setList(list);
excelParams.add(excelEntity);
} else {
List<Method> newMethods = new ArrayList<Method>();
if (getMethods != null) {
newMethods.addAll(getMethods);
}
newMethods.add(PoiReflectorUtil.fromCache(pojoClass).getGetMethod(field.getName()));
ExcelEntity excel = field.getAnnotation(ExcelEntity.class);
if (excel.show() && StringUtils.isEmpty(excel.name())) {
throw new ExcelExportException("if use ExcelEntity ,name mus has value ,data: " + ReflectionToStringBuilder.toString(excel), ExcelExportEnum.PARAMETER_ERROR);
}
getAllExcelField(exclusions,
StringUtils.isNotEmpty(excel.id()) ? excel.id() : targetId,
PoiPublicUtil.getClassFields(field.getType()), excelParams, field.getType(),
newMethods, excel.show() ? excel : null);
}
}
}
所以,也需要对 getAllExcelField方法进行重载。
二、自定义导出类CustomizeExportService代码
经过上述源码分析后,我们就可以通过继承ExcelExportService类并对其部分方法进行重写和重载,从而完成自定义样式开发。相关代码如下
1、CustomizeExportService类
import cn.afterturn.easypoi.excel.annotation.Excel;
import cn.afterturn.easypoi.excel.annotation.ExcelCollection;
import cn.afterturn.easypoi.excel.annotation.ExcelEntity;
import cn.afterturn.easypoi.excel.annotation.ExcelTarget;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
import cn.afterturn.easypoi.excel.export.ExcelExportService;
import cn.afterturn.easypoi.excel.export.styler.IExcelExportStyler;
import cn.afterturn.easypoi.exception.excel.ExcelExportException;
import cn.afterturn.easypoi.exception.excel.enums.ExcelExportEnum;
import cn.afterturn.easypoi.util.PoiExcelGraphDataUtil;
import cn.afterturn.easypoi.util.PoiMergeCellUtil;
import cn.afterturn.easypoi.util.PoiPublicUtil;
import cn.afterturn.easypoi.util.PoiReflectorUtil;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.apache.poi.xssf.usermodel.XSSFColor;
import org.apache.poi.xssf.usermodel.XSSFDataValidation;
import org.dromara.common.excel.annotation.HeadFontStyle;
import org.dromara.common.excel.annotation.HeadStyle;
import org.dromara.common.excel.domain.ExcelExportExtendEntity;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
public class CustomizeExportService extends ExcelExportService {
/**
* 最大行数,超过自动多Sheet
*/
private static int MAX_NUM = 60000;
/**
* 获取需要导出的全部字段
*
* @param targetId 目标ID
*/
public void getAllExcelField(ExportParams entity, String targetId, Field[] fields,
List<ExcelExportEntity> excelParams, List<ExcelExportExtendEntity> extendParamsList, Class<?> pojoClass,
List<Method> getMethods, ExcelEntity excelGroup, Workbook workbook) throws Exception {
List<String> exclusionsList = entity.getExclusions() != null ? Arrays.asList(entity.getExclusions()) : null;
// 遍历整个filed
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
// 先判断是不是collection,在判断是不是java自带对象,之后就是我们自己的对象了
if (PoiPublicUtil.isNotUserExcelUserThis(exclusionsList, field, targetId)) {
continue;
}
// 首先判断Excel 可能一下特殊数据用户回自定义处理
if (field.getAnnotation(Excel.class) != null) {
Excel excel = field.getAnnotation(Excel.class);
String name = PoiPublicUtil.getValueByTargetId(excel.name(), targetId, null);
if (StringUtils.isNotBlank(name)) {
excelParams.add(createExcelExportEntity(field, targetId, pojoClass, getMethods, excelGroup));
extendParamsList.add(createExcelExportExtendEntity(entity, field, pojoClass, workbook));
}
} else if (PoiPublicUtil.isCollection(field.getType())) {
ExcelCollection excel = field.getAnnotation(ExcelCollection.class);
ParameterizedType pt = (ParameterizedType) field.getGenericType();
Class<?> clz = (Class<?>) pt.getActualTypeArguments()[0];
List<ExcelExportEntity> list = new ArrayList<ExcelExportEntity>();
List<ExcelExportExtendEntity> extendList = new ArrayList<>();
getAllExcelField(entity,
StringUtils.isNotEmpty(excel.id()) ? excel.id() : targetId,
PoiPublicUtil.getClassFields(clz), list, extendList, clz, null, null, workbook);
//ExcelExportEntity
ExcelExportEntity excelEntity = new ExcelExportEntity();
excelEntity.setName(PoiPublicUtil.getValueByTargetId(excel.name(), targetId, null));
if (i18nHandler != null) {
excelEntity.setName(i18nHandler.getLocaleName(excelEntity.getName()));
}
excelEntity.setOrderNum(Integer
.valueOf(PoiPublicUtil.getValueByTargetId(excel.orderNum(), targetId, "0")));
excelEntity
.setMethod(PoiReflectorUtil.fromCache(pojoClass).getGetMethod(field.getName()));
excelEntity.setList(list);
excelParams.add(excelEntity);
//ExcelExportExtendEntity
ExcelExportExtendEntity extendEntity = createExcelExportExtendEntity(entity, field, pojoClass, workbook);
extendEntity.setExtendList(extendList);
extendParamsList.add(extendEntity);
} else {
List<Method> newMethods = new ArrayList<Method>();
if (getMethods != null) {
newMethods.addAll(getMethods);
}
newMethods.add(PoiReflectorUtil.fromCache(pojoClass).getGetMethod(field.getName()));
ExcelEntity excel = field.getAnnotation(ExcelEntity.class);
if (excel.show() && StringUtils.isEmpty(excel.name())) {
throw new ExcelExportException("if use ExcelEntity ,name mus has value ,data: " + ReflectionToStringBuilder.toString(excel), ExcelExportEnum.PARAMETER_ERROR);
}
getAllExcelField(entity,
StringUtils.isNotEmpty(excel.id()) ? excel.id() : targetId,
PoiPublicUtil.getClassFields(field.getType()), excelParams, extendParamsList, field.getType(),
newMethods, excel.show() ? excel : null, workbook);
}
}
}
protected void insertDataToSheet(Workbook workbook, ExportParams entity, List<ExcelExportEntity> entityList, List<ExcelExportExtendEntity> extendList, Collection<?> dataSet, Sheet sheet) {
try {
dataHandler = entity.getDataHandler();
if (dataHandler != null && dataHandler.getNeedHandlerFields() != null) {
needHandlerList = Arrays.asList(dataHandler.getNeedHandlerFields());
}
dictHandler = entity.getDictHandler();
commentHandler = entity.getCommentHandler();
Drawing patriarch = PoiExcelGraphDataUtil.getDrawingPatriarch(sheet);
List<ExcelExportEntity> excelParams = new ArrayList<ExcelExportEntity>();
if (entity.isAddIndex()) {
excelParams.add(indexExcelEntity(entity));
}
excelParams.addAll(entityList);
sortAllParams(excelParams);
int index = entity.isCreateHeadRows()
? createHeaderAndTitle(entity, sheet, workbook, excelParams, extendList) : 0;
int titleHeight = index;
setCellWith(excelParams, sheet);
setColumnHidden(excelParams, sheet);
short rowHeight = entity.getHeight() != 0 ? entity.getHeight() : getRowHeight(excelParams);
setCurrentIndex(1);
createAddressList(sheet, index, excelParams, 0);
Iterator<?> its = dataSet.iterator();
List<Object> tempList = new ArrayList<Object>();
while (its.hasNext()) {
Object t = its.next();
index += createCells(patriarch, index, t, excelParams, sheet, workbook, rowHeight, 0)[0];
tempList.add(t);
if (index >= MAX_NUM) {
break;
}
}
if (entity.getFreezeCol() != 0) {
sheet.createFreezePane(entity.getFreezeCol(), 0, entity.getFreezeCol(), 0);
}
mergeCells(sheet, excelParams, titleHeight);
its = dataSet.iterator();
for (int i = 0, le = tempList.size(); i < le; i++) {
its.next();
its.remove();
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("List data more than max ,data size is {}",
dataSet.size());
}
// 发现还有剩余list 继续循环创建Sheet
if (dataSet.size() > 0) {
createSheetForMap(workbook, entity, entityList, dataSet);
} else {
// 创建合计信息
addStatisticsRow(getExcelExportStyler().getStyles(true, null), sheet);
}
} catch (Exception e) {
LOGGER.error(e.getMessage(), e);
throw new ExcelExportException(ExcelExportEnum.EXPORT_ERROR, e);
}
}
@Override
public void createSheet(Workbook workbook, ExportParams entity, Class<?> pojoClass, Collection<?> dataSet) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Excel export start ,class is {}", pojoClass);
LOGGER.debug("Excel version is {}",
entity.getType().equals(ExcelType.HSSF) ? "03" : "07");
}
if (workbook == null || entity == null || pojoClass == null || dataSet == null) {
throw new ExcelExportException(ExcelExportEnum.PARAMETER_ERROR);
}
try {
// 创建表格样式
setExcelExportStyler((IExcelExportStyler) entity.getStyle()
.getConstructor(Workbook.class).newInstance(workbook));
List<ExcelExportEntity> excelParams = new ArrayList<>();
List<ExcelExportExtendEntity> extendList = new ArrayList<>();
i18nHandler = entity.getI18nHandler();
// 得到所有字段
Field[] fields = PoiPublicUtil.getClassFields(pojoClass);
ExcelTarget excelTarget = pojoClass.getAnnotation(ExcelTarget.class);
String targetId = excelTarget == null ? null : excelTarget.value();
getAllExcelField(entity, targetId, fields, excelParams, extendList, pojoClass,
null, null, workbook);
//获取所有参数后,后面的逻辑判断就一致了
createSheetForMap(workbook, entity, excelParams, extendList, dataSet);
} catch (Exception e) {
LOGGER.error(e.getMessage(), e);
throw new ExcelExportException(ExcelExportEnum.EXPORT_ERROR, e);
}
}
public void createSheetForMap(Workbook workbook, ExportParams entity,
List<ExcelExportEntity> entityList, List<ExcelExportExtendEntity> extendList, Collection<?> dataSet) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Excel version is {}",
entity.getType().equals(ExcelType.HSSF) ? "03" : "07");
}
if (workbook == null || entity == null || entityList == null || dataSet == null) {
throw new ExcelExportException(ExcelExportEnum.PARAMETER_ERROR);
}
super.type = entity.getType();
if (type.equals(ExcelType.XSSF)) {
MAX_NUM = 1000000;
}
if (entity.getMaxNum() > 0) {
MAX_NUM = entity.getMaxNum();
}
Sheet sheet = null;
try {
sheet = workbook.createSheet(entity.getSheetName());
} catch (Exception e) {
// 重复遍历,出现了重名现象,创建非指定的名称Sheet
sheet = workbook.createSheet();
}
if (dataSet.getClass().getClass().getName().contains("Unmodifiable")) {
List dataTemp = new ArrayList<>();
dataTemp.addAll(dataSet);
dataSet = dataTemp;
}
insertDataToSheet(workbook, entity, entityList, extendList, dataSet, sheet);
if (entity.isReadonly()) {
sheet.protectSheet(UUID.randomUUID().toString());
}
sheet.setForceFormulaRecalculation(true);
if (isAutoSize(entity, entityList)) {
for (int len = Math.max(sheet.getRow(0).getLastCellNum(), sheet.getRow(1).getLastCellNum()), i = 0; i < len; i++) {
sheet.autoSizeColumn(i, true);
}
}
}
private int createHeaderAndTitle(ExportParams entity, Sheet sheet, Workbook workbook,
List<ExcelExportEntity> excelParams, List<ExcelExportExtendEntity> extendList) {
int rows = 0, fieldLength = getFieldLength(excelParams);
if (entity.getTitle() != null) {
rows += createTitle2Row(entity, sheet, workbook, fieldLength);
}
createHeaderRow(entity, sheet, workbook, rows, excelParams, extendList, 0);
rows += getRowNums(excelParams, true);
if (entity.isFixedTitle()) {
sheet.createFreezePane(0, rows, 0, rows);
}
return rows;
}
/**
* 创建表头
*
* @param title
* @param sheet
* @param workbook
* @param index
* @param excelParams excel字段标准信息
* @param extendList excel字段额外信息
* @param cellIndex
* @return
*/
private int createHeaderRow(ExportParams title, Sheet sheet, Workbook workbook, int index,
List<ExcelExportEntity> excelParams, List<ExcelExportExtendEntity> extendList, int cellIndex) {
Row row = sheet.getRow(index) == null ? sheet.createRow(index) : sheet.getRow(index);
int rows = getRowNums(excelParams, true);
row.setHeight(title.getHeaderHeight());
Row listRow = null;
if (rows >= 2) {
listRow = sheet.getRow(index + 1);
if (listRow == null) {
listRow = sheet.createRow(index + 1);
listRow.setHeight(title.getHeaderHeight());
}
}
int groupCellLength = 0;
// CellStyle titleStyle = getExcelExportStyler().getTitleStyle(title.getColor());
for (int i = 0, exportFieldTitleSize = excelParams.size(); i < exportFieldTitleSize; i++) {
ExcelExportEntity entity = excelParams.get(i);
ExcelExportExtendEntity extendEntity = extendList.get(i);
CellStyle headerCellStyle = extendEntity.getHeaderCellStyle();
// 加入换了groupName或者结束就,就把之前的那个换行
if (StringUtils.isBlank(entity.getGroupName()) || i == 0 || !entity.getGroupName().equals(excelParams.get(i - 1).getGroupName())) {
if (groupCellLength > 1) {
sheet.addMergedRegion(new CellRangeAddress(index, index, cellIndex - groupCellLength, cellIndex - 1));
}
groupCellLength = 0;
}
if (StringUtils.isNotBlank(entity.getGroupName())) {
createStringCell(row, cellIndex, entity.getGroupName(), headerCellStyle, entity);
createStringCell(listRow, cellIndex, entity.getName(), headerCellStyle, entity);
groupCellLength++;
} else if (StringUtils.isNotBlank(entity.getName())) {
createStringCell(row, cellIndex, entity.getName(), headerCellStyle, entity);
}
if (entity.getList() != null) {
// 保持原来的
int tempCellIndex = cellIndex;
cellIndex = createHeaderRow(title, sheet, workbook, rows == 1 ? index : index + 1, entity.getList(), extendEntity.getExtendList(), cellIndex);
List<ExcelExportEntity> sTitel = entity.getList();
if (StringUtils.isNotBlank(entity.getName()) && sTitel.size() > 1) {
PoiMergeCellUtil.addMergedRegion(sheet, index, index, tempCellIndex, tempCellIndex + getFieldLength(sTitel));
}
/*for (int j = 0, size = sTitel.size(); j < size; j++) {
createStringCell(rows == 2 ? listRow : row, cellIndex, sTitel.get(j).getName(),
titleStyle, entity);
cellIndex++;
}*/
cellIndex--;
} else if (rows > 1 && StringUtils.isBlank(entity.getGroupName())) {
createStringCell(listRow, cellIndex, "", headerCellStyle, entity);
PoiMergeCellUtil.addMergedRegion(sheet, index, index + rows - 1, cellIndex, cellIndex);
}
cellIndex++;
}
if (groupCellLength > 1) {
PoiMergeCellUtil.addMergedRegion(sheet, index, index, cellIndex - groupCellLength, cellIndex - 1);
}
return cellIndex;
}
private int createAddressList(Sheet sheet, int index, List<ExcelExportEntity> excelParams, int cellIndex) {
for (int i = 0; i < excelParams.size(); i++) {
if (excelParams.get(i).isAddressList()) {
ExcelExportEntity entity = excelParams.get(i);
CellRangeAddressList regions = new CellRangeAddressList(index,
this.type.equals(ExcelType.XSSF) ? 1000000 : 65535, cellIndex, cellIndex);
DataValidation dataValidation = sheet.getDataValidationHelper().createValidation(sheet.getDataValidationHelper().createExplicitListConstraint(getAddressListValues(entity)), regions);
// 处理Excel兼容性问题
if (dataValidation instanceof XSSFDataValidation) {
dataValidation.setSuppressDropDownArrow(true);
dataValidation.setShowErrorBox(true);
} else {
dataValidation.setSuppressDropDownArrow(false);
}
sheet.addValidationData(dataValidation);
}
if (excelParams.get(i).getList() != null) {
cellIndex = createAddressList(sheet, index, excelParams.get(i).getList(), cellIndex);
} else {
cellIndex++;
}
}
return cellIndex;
}
private String[] getAddressListValues(ExcelExportEntity entity) {
if (StringUtils.isNotEmpty(entity.getDict())) {
String[] arr = new String[this.dictHandler.getList(entity.getDict()).size()];
for (int i = 0; i < arr.length; i++) {
arr[i] = this.dictHandler.getList(entity.getDict()).get(i).get("dictValue").toString();
}
return arr;
} else if (entity.getReplace() != null && entity.getReplace().length > 0) {
String[] arr = new String[entity.getReplace().length];
for (int i = 0; i < arr.length; i++) {
arr[i] = entity.getReplace()[i].split("_")[0];
}
return arr;
}
throw new ExcelExportException(entity.getName() + "没有可以创建下来的数据,请addressList不要设置为true");
}
private ExcelExportExtendEntity createExcelExportExtendEntity(ExportParams exportParams, Field field, Class<?> pojoClass, Workbook workbook) {
ExcelExportExtendEntity entity = new ExcelExportExtendEntity();
entity.setFieldName(field.getName());
CellStyle headCellStyle = getExcelExportStyler().getTitleStyle(exportParams.getColor());
setHeadStyle(headCellStyle, field, pojoClass, workbook);//设置表头样式
setHeadFont(headCellStyle, field, pojoClass, workbook);//设置表头字体
entity.setHeaderCellStyle(headCellStyle);
return entity;
}
/**
* 设置表头字体
*
* @param headCellStyle
* @param field
*/
private void setHeadFont(CellStyle headCellStyle, Field field, Class<?> pojoClass, Workbook workbook) {
HeadFontStyle fontStyle = field.getAnnotation(HeadFontStyle.class);
if (null == fontStyle)
fontStyle = pojoClass.getAnnotation(HeadFontStyle.class);
if (null != fontStyle) {
Font font = workbook.createFont();
font.setBold(fontStyle.bold());
if (fontStyle.charset() != -1)
font.setCharSet(fontStyle.charset());
if (fontStyle.color() != -1)
font.setColor(fontStyle.color());
if (fontStyle.fontHeightInPoints() != -1)
font.setFontHeightInPoints(fontStyle.fontHeightInPoints());
if (StringUtils.isNotBlank(fontStyle.fontName()))
font.setFontName(fontStyle.fontName());
font.setItalic(fontStyle.italic());
font.setStrikeout(fontStyle.strikeout());
if (fontStyle.typeOffset() != -1)
font.setTypeOffset(fontStyle.typeOffset());
if (fontStyle.underline() != -1)
font.setUnderline(fontStyle.underline());
headCellStyle.setFont(font);
}
}
/**
* 设置表头样式
*
* @param headCellStyle
* @param field
*/
private void setHeadStyle(CellStyle headCellStyle, Field field, Class<?> pojoClass, Workbook workbook) {
HeadStyle headStyle = field.getAnnotation(HeadStyle.class);
if (null == headStyle)
headStyle = pojoClass.getAnnotation(HeadStyle.class);
if (null != headStyle) {
headCellStyle.setFillPattern(headStyle.fillPatternType());
int[] colorBackArr = headStyle.fillBackgroundColorRGB();
if (colorBackArr.length == 3) {
XSSFColor color = new XSSFColor();
color.setRGB(intToByteArray(getIntFromColor(colorBackArr[0], colorBackArr[1], colorBackArr[2])));
headCellStyle.setFillBackgroundColor(color);
}
int[] colorForeArr = headStyle.fillForegroundColorRGB();
if (colorForeArr.length == 3) {
XSSFColor color = new XSSFColor();
color.setRGB(intToByteArray(getIntFromColor(colorForeArr[0], colorForeArr[1], colorForeArr[2])));
headCellStyle.setFillForegroundColor(color);
}
headCellStyle.setBorderLeft(headStyle.borderLeft());
headCellStyle.setBorderRight(headStyle.borderRight());
headCellStyle.setBorderTop(headStyle.borderTop());
headCellStyle.setBorderBottom(headStyle.borderBottom());
if (headStyle.leftBorderColor() != -1)
headCellStyle.setLeftBorderColor(headStyle.leftBorderColor());
if (headStyle.rightBorderColor() != -1)
headCellStyle.setRightBorderColor(headStyle.rightBorderColor());
if (headStyle.topBorderColor() != -1)
headCellStyle.setTopBorderColor(headStyle.topBorderColor());
if (headStyle.bottomBorderColor() != -1)
headCellStyle.setBottomBorderColor(headStyle.bottomBorderColor());
if (headStyle.fillBackgroundColor() != -1) {
headCellStyle.setFillBackgroundColor(headStyle.fillBackgroundColor());
}
if (headStyle.fillForegroundColor() != -1) {
headCellStyle.setFillForegroundColor(headStyle.fillForegroundColor());
}
}
}
private ExcelExportEntity createExcelExportEntity(Field field, String targetId,
Class<?> pojoClass,
List<Method> getMethods, ExcelEntity excelGroup) throws Exception {
Excel excel = field.getAnnotation(Excel.class);
ExcelExportEntity excelEntity = new ExcelExportEntity();
excelEntity.setType(excel.type());
getExcelField(targetId, field, excelEntity, excel, pojoClass, excelGroup);
if (getMethods != null) {
List<Method> newMethods = new ArrayList<Method>();
newMethods.addAll(getMethods);
newMethods.add(excelEntity.getMethod());
excelEntity.setMethods(newMethods);
}
return excelEntity;
}
/**
* 注解到导出对象的转换
*/
private void getExcelField(String targetId, Field field, ExcelExportEntity excelEntity,
Excel excel, Class<?> pojoClass, ExcelEntity excelGroup) throws Exception {
excelEntity.setName(PoiPublicUtil.getValueByTargetId(excel.name(), targetId, null));
excelEntity.setKey(field.getName());
excelEntity.setWidth(excel.width());
excelEntity.setHeight(excel.height());
excelEntity.setNeedMerge(excel.needMerge());
excelEntity.setMergeVertical(excel.mergeVertical());
excelEntity.setMergeRely(excel.mergeRely());
excelEntity.setReplace(excel.replace());
excelEntity.setOrderNum(
Integer.valueOf(PoiPublicUtil.getValueByTargetId(excel.orderNum(), targetId, "0")));
excelEntity.setWrap(excel.isWrap());
excelEntity.setExportImageType(excel.imageType());
excelEntity.setSuffix(excel.suffix());
excelEntity.setDatabaseFormat(excel.databaseFormat());
excelEntity.setFormat(
StringUtils.isNotEmpty(excel.exportFormat()) ? excel.exportFormat() : excel.format());
excelEntity.setStatistics(excel.isStatistics());
excelEntity.setHyperlink(excel.isHyperlink());
excelEntity.setMethod(PoiReflectorUtil.fromCache(pojoClass).getGetMethod(field.getName()));
excelEntity.setNumFormat(excel.numFormat());
excelEntity.setColumnHidden(excel.isColumnHidden());
excelEntity.setDict(excel.dict());
excelEntity.setEnumExportField(excel.enumExportField());
excelEntity.setTimezone(excel.timezone());
excelEntity.setAddressList(excel.addressList());
excelEntity.setDesensitizationRule(excel.desensitizationRule());
if (excelGroup != null) {
excelEntity.setGroupName(PoiPublicUtil.getValueByTargetId(excelGroup.name(), targetId, null));
} else {
excelEntity.setGroupName(excel.groupName());
}
if (i18nHandler != null) {
excelEntity.setName(i18nHandler.getLocaleName(excelEntity.getName()));
excelEntity.setGroupName(i18nHandler.getLocaleName(excelEntity.getGroupName()));
}
}
private boolean isAutoSize(ExportParams entity, List<ExcelExportEntity> entityList) {
if (entity.isAutoSize()) {
return true;
}
AtomicBoolean autoSize = new AtomicBoolean(true);
entityList.forEach(e -> {
if (e.getWidth() != 10) {
autoSize.set(false);
return;
}
if (CollectionUtils.isNotEmpty(e.getList()) && !isAutoSize(entity, e.getList())) {
autoSize.set(false);
return;
}
});
return autoSize.get();
}
/**
* rgb转int
*/
private static int getIntFromColor(int Red, int Green, int Blue) {
Red = (Red << 16) & 0x00FF0000;
Green = (Green << 8) & 0x0000FF00;
Blue = Blue & 0x000000FF;
return 0xFF000000 | Red | Green | Blue;
}
/**
* int转byte[]
*/
public static byte[] intToByteArray(int i) {
byte[] result = new byte[4];
result[0] = (byte) ((i >> 24) & 0xFF);
result[1] = (byte) ((i >> 16) & 0xFF);
result[2] = (byte) ((i >> 8) & 0xFF);
result[3] = (byte) (i & 0xFF);
return result;
}
}
2、自定义样式的存储类ExcelExportExtendEntity:
import lombok.Data;
import org.apache.poi.ss.usermodel.CellStyle;
import java.util.List;
@Data
public class ExcelExportExtendEntity {
/**
* 字段名称
*/
private String fieldName;
/**
* 表头样式
*/
private CellStyle headerCellStyle;
/**
* 子表头样式
*/
private List<ExcelExportExtendEntity> extendList;
}
3、自定义注解HeadFontStyle、HeadStyle
import org.apache.poi.common.usermodel.fonts.FontCharset;
import org.apache.poi.hssf.usermodel.HSSFPalette;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.IndexedColors;
import java.lang.annotation.*;
@Target({ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface HeadFontStyle {
/**
* The name for the font (i.e. Arial)
*/
String fontName() default "";
/**
* Height in the familiar unit of measure - points
*/
short fontHeightInPoints() default -1;
/**
* Whether to use italics or not
*/
boolean italic() default false;
/**
* Whether to use a strikeout horizontal line through the text or not
*/
boolean strikeout() default false;
/**
* The color for the font
*
* @see Font#COLOR_NORMAL
* @see Font#COLOR_RED
* @see HSSFPalette#getColor(short)
* @see IndexedColors
*/
short color() default -1;
/**
* Set normal, super or subscript.
*
* @see Font#SS_NONE
* @see Font#SS_SUPER
* @see Font#SS_SUB
*/
short typeOffset() default -1;
/**
* set type of text underlining to use
*
* @see Font#U_NONE
* @see Font#U_SINGLE
* @see Font#U_DOUBLE
* @see Font#U_SINGLE_ACCOUNTING
* @see Font#U_DOUBLE_ACCOUNTING
*/
byte underline() default -1;
/**
* Set character-set to use.
*
* @see FontCharset
* @see Font#ANSI_CHARSET
* @see Font#DEFAULT_CHARSET
* @see Font#SYMBOL_CHARSET
*/
int charset() default -1;
/**
* Bold
*/
boolean bold() default false;
}
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.FillPatternType;
import org.apache.poi.ss.usermodel.IndexedColors;
import java.lang.annotation.*;
@Target({ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface HeadStyle {
/**
* Set the type of border to use for the left border of the cell
*/
BorderStyle borderLeft() default BorderStyle.THIN;
/**
* Set the type of border to use for the right border of the cell
*/
BorderStyle borderRight() default BorderStyle.THIN;
/**
* Set the type of border to use for the top border of the cell
*/
BorderStyle borderTop() default BorderStyle.THIN;
/**
* Set the type of border to use for the bottom border of the cell
*/
BorderStyle borderBottom() default BorderStyle.THIN;
/**
* Set the color to use for the left border
*
* @see IndexedColors
*/
short leftBorderColor() default 54;
/**
* Set the color to use for the right border
*
* @see IndexedColors
*/
short rightBorderColor() default 54;
/**
* Set the color to use for the top border
*
* @see IndexedColors
*/
short topBorderColor() default 54;
/**
* Set the color to use for the bottom border
*
* @see IndexedColors
*/
short bottomBorderColor() default 54;
/**
* Setting to one fills the cell with the foreground color... No idea about other values
*
* @see FillPatternType#SOLID_FOREGROUND
*/
FillPatternType fillPatternType() default FillPatternType.SOLID_FOREGROUND;
/**
* Set the background fill color.
*
* @see IndexedColors
*/
short fillBackgroundColor() default -1;
/**
* 按RGB顺序
*
* @return rgb颜色
*/
int[] fillBackgroundColorRGB() default {};
/**
* Set the foreground fill color <i>Note: Ensure Foreground color is set prior to background color.</i>
*
* @see IndexedColors
*/
short fillForegroundColor() default -1;
/**
* 按RGB顺序
*
* @return rgb颜色
*/
int[] fillForegroundColorRGB() default {};
}
4、工具类EasyPoiExcelUtil
此类是调用自定义导出类来完成excel导出功能。
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import cn.hutool.core.util.IdUtil;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.dromara.common.core.utils.file.FileUtils;
import org.dromara.common.excel.service.easypoi.CustomizeExportService;
import javax.servlet.http.HttpServletResponse;
import java.io.UnsupportedEncodingException;
import java.util.List;
public class EasyPoiExcelUtil {
public static void exportExcel(String title, String sheetName, List<?> list, Class<?> pojoClass, HttpServletResponse response) {
try {
resetResponse(sheetName, response);
ExportParams exportParams = new ExportParams(title, sheetName);
Workbook sheets = getWorkbook(exportParams.getType(), list.size());
new CustomizeExportService().createSheet(sheets, exportParams, pojoClass, list);
// Workbook sheets = ExcelExportUtil.exportExcel(exportParams, pojoClass, list);
sheets.write(response.getOutputStream());
sheets.close();
} catch (Exception e) {
throw new RuntimeException("导出Excel异常", e);
}
}
/**
* 重置响应体
*/
private static void resetResponse(String sheetName, HttpServletResponse response) throws UnsupportedEncodingException {
String filename = encodingFilename(sheetName);
FileUtils.setAttachmentResponseHeader(response, filename);
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8");
}
/**
* 编码文件名
*/
public static String encodingFilename(String filename) {
return IdUtil.fastSimpleUUID() + "_" + filename + ".xlsx";
}
private static Workbook getWorkbook(ExcelType type, int size) {
if (ExcelType.HSSF.equals(type)) {
return new HSSFWorkbook();
} else {
return new XSSFWorkbook();
}
}
}
三、仍需改进
目前只自定义了表头字体及样式,若要支持自定义内容字体及样式,则只需要在ExcelExportExtendEntity类中增加内容字体的字段,并按照相同逻辑存储并修改insertDataToSheet方法。