共计 6329 个字符,预计需要花费 16 分钟才能阅读完成。
EasyExcel 是阿里巴巴对 POI 封装的一个库,号称解决了 POI 的 OOM 问题,并且在使用上也更方便一些 Github:[](https://github.com/alibaba/ea…
然而我在使用的时候发现还是有很多坑,其中一个比较头疼的是对单个单元格样式的设置。EasyExcel 提供了一个 BaseRowModel 作为每行数据的一个模型,并且其中有一个属性 cellStyleMap 代表每列样式的集合,本来我以为这个只要在自己定义模型的时候,也把 CellStyle 定义进去就行了,然而,还是我想多了……定义了 CellStyle 并没有什么卵用,这是第一个蛋疼的地方
/**
* Excel 基础模型
* @author jipengfei
*/
public class BaseRowModel {
/**
* 每列样式
*/
private Map<Integer,CellStyle> cellStyleMap = new HashMap<Integer,CellStyle>();
public void addStyle(Integer row, CellStyle cellStyle){
cellStyleMap.put(row,cellStyle);
}
public CellStyle getStyle(Integer row){
return cellStyleMap.get(row);
}
public Map<Integer, CellStyle> getCellStyleMap() {
return cellStyleMap;
}
public void setCellStyleMap(Map<Integer, CellStyle> cellStyleMap) {
this.cellStyleMap = cellStyleMap;
}
}
后来测试半天,才发现创建 CellStyle 时必须通过一个 Workbook 对象来创建,而这个 Workbook 不能随便新建一个对象完事儿,得用你当前写入的 Workbook 来创建对应的 CellStyle 样式才能起作用。然而……事情并没有那么简单,经过我对源码的反复查看,EasyExcel 生成 excel 表的步骤是用一个 ExcelWriter 来写入数据,并没有提供获取 Workbook 的方法,不知道什么原因让阿里巴巴不提供这样一个接口……这是第二个蛋疼的地方
既然没有提供接口,那就只能用反射来硬刚了,下面就直接上代码了
我这里是在开始写数据之前就将每张表的 CellStyle 与每张表关联起来,再在后面的 handler 中获取到这个 CellStyle 进行设置
package edu.uddp.util;
import com.alibaba.excel.EasyExcelFactory;
import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.context.WriteContext;
import com.alibaba.excel.event.WriteHandler;
import com.alibaba.excel.metadata.BaseRowModel;
import com.alibaba.excel.metadata.Sheet;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.ExcelBuilderImpl;
import com.sun.corba.se.spi.orbutil.threadpool.Work;
import edu.uddp.enums.CellStyleEnum;
import edu.uddp.model.SignExcelRow;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import java.io.*;
import java.lang.reflect.Field;
import java.util.*;
/**
* 生成 Excel 表
*
* @author Juzi
* @date 2018/12/23 12:37
* Blog https://juzibiji.top
*/
public class ExcelUtil {
private static Map<Workbook, Map<String, CellStyle>> cellStyles = new HashMap<>();
/**
* 使用 java 对象模型创建 excel,并使用 handler
* 生成 Excel 格式为 xlsx
*
* @param path Excel 生成路径
* @param headLineMun 表头占几行
* @param data 传入的键值对数据(key 为 sheet 名,value 为 sheet 数据)
* @param handler 自定义的 EasyExcel Handler,不使用传入 null 即可
* @param columnWidthMap 每列宽度
* @throws IOException
*/
public static void createExcelWithModelAndHandler(
String path, int headLineMun, Map<String,
List<? extends BaseRowModel>> data, WriteHandler handler,
Map<Integer, Integer> columnWidthMap, List<CellStyleEnum> cellStyleEnums) throws IOException {
File file = new File(path);
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
OutputStream out = new FileOutputStream(path);
// ExcelWriter 用于导出 Excel
ExcelWriter writer = EasyExcelFactory.getWriterWithTempAndHandler(null, out,
ExcelTypeEnum.XLSX, true, handler);
// 构造单元格样式
Workbook workbook = getWorkbook(writer);
cellStyles.put(workbook, createCellStyle(workbook, cellStyleEnums));
// sheet 的序号,从 1 开始
int i = 1;
// 遍历传入的 sheet 名和 sheet 数据来创建 sheet
for (Map.Entry<String, List<? extends BaseRowModel>> entry : data.entrySet()) {
Sheet sheet = new Sheet(i, headLineMun, entry.getValue().get(0).getClass(), entry.getKey(), null);
sheet.setColumnWidthMap(columnWidthMap);
writer.write(entry.getValue(), sheet);
i++;
}
// 必须要调用 finish(),否则数据不会写入文件
writer.finish();
out.close();
}
/**
* ** 获取 workbook**
* 因为 EasyExcel 这个库设计的原因
* 只能使用反射获取 workbook
*
* @param writer
* @return
*/
private static Workbook getWorkbook(ExcelWriter writer) {
Workbook workbook = null;
try {
Class<?> clazz1 = Class.forName(“com.alibaba.excel.ExcelWriter”);
Field[] fs = clazz1.getDeclaredFields();
for (Field field : fs) {
// 要设置属性可达,不然会抛出 IllegalAccessException 异常
field.setAccessible(true);
if (“excelBuilder”.equals(field.getName())) {
ExcelBuilderImpl excelBuilder = (ExcelBuilderImpl) field.get(writer);
Class<?> clazz2 = Class.forName(“com.alibaba.excel.write.ExcelBuilderImpl”);
Field[] fs2 = clazz2.getDeclaredFields();
for (Field field2 : fs2) {
field2.setAccessible(true);
if (“context”.equals(field2.getName())) {
WriteContext context = (WriteContext) field2.get(excelBuilder);
workbook = context.getWorkbook();
}
}
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return workbook;
}
public static Map createCellStyle(Workbook workbook, List<CellStyleEnum> cellStyleEnums) {
Map<String, CellStyle> map = new HashMap<>();
for (CellStyleEnum cellStyleEnum : cellStyleEnums) {
if (cellStyleEnum.getNo() == 1) {
CellStyle cellStyle = workbook.createCellStyle();
cellStyle.setBorderBottom(BorderStyle.THIN); // 下边框
cellStyle.setBorderLeft(BorderStyle.THIN);// 左边框
cellStyle.setBorderTop(BorderStyle.THIN);// 上边框
cellStyle.setBorderRight(BorderStyle.THIN);// 右边框
cellStyle.setAlignment(HorizontalAlignment.CENTER);
map.put(cellStyleEnum.getName(), cellStyle);
} else if (cellStyleEnum.getNo() == 2) {
CellStyle cellStyle = workbook.createCellStyle();
cellStyle.setBorderBottom(BorderStyle.THIN); // 下边框
cellStyle.setBorderLeft(BorderStyle.THIN);// 左边框
cellStyle.setBorderTop(BorderStyle.THIN);// 上边框
cellStyle.setBorderRight(BorderStyle.THIN);// 右边框
cellStyle.setAlignment(HorizontalAlignment.CENTER);
cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
cellStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
map.put(cellStyleEnum.getName(), cellStyle);
}
}
return map;
}
public static Map<Workbook, Map<String, CellStyle>> getCellStyles() {
return cellStyles;
}
}
EasyExcel 提供了一个 WriteHandler,我们实现这个接口,就可以在每个单元格写入之后或者每行写入之前来进行拦截(这个 handler 设计的也很蛋疼),并注入我们自己的业务逻辑(设置单元格样式)。
package edu.uddp.handler;
import com.alibaba.excel.event.WriteHandler;
import edu.uddp.util.ExcelUtil;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import java.util.Map;
/**
* 第三方库 EasyExcel 的 Handler
* 教师端生成签到历史表 Excel 时
* 将未签到学生进行特殊标识
*
* @author Juzi
* @since 2018/12/22 22:07
* Blog https://juzibiji.top
*/
public class SignRecordExcelHandler implements WriteHandler {
@Override
public void sheet(int i, Sheet sheet) {
}
@Override
public void row(int i, Row row) {
}
@Override
public void cell(int i, Cell cell) {
// 获取当前 workbook 对应的 CellStyle 集合
Map<String, CellStyle> cellStyleMap = ExcelUtil.getCellStyles().get(cell.getSheet().getWorkbook());
// 从第二行开始设置格式,第一行是表头
if (cell.getRowIndex() > 0) {
if (i == 7 && “ 未签到 ”.equals(cell.getStringCellValue())) {
// 该生未签到
for (int j = 0; j < 8; j++) {
cell.getRow().getCell(j).setCellStyle(cellStyleMap.get(“ 未签到 ”));
}
} else if (i == 8 && “ 已签到 ”.equals(cell.getRow().getCell(7).getStringCellValue())) {
// 该生已签到
for (int j = 0; j < 9; j++) {
cell.getRow().getCell(j).setCellStyle(cellStyleMap.get(“ 已签到 ”));
}
}else if(i == 8 && “ 未签到 ”.equals(cell.getRow().getCell(7).getStringCellValue())){
cell.setCellStyle(cellStyleMap.get(“ 已签到 ”));
}
}
}
}
上面有一些简单的逻辑处理,就不一一介绍了。
若文章有任何问题,欢迎留言指出——作者博客:桔子笔记