EasyExcel是阿里巴巴對POI封裝的一個庫,號稱解決了POI的OOM問題,而且在使用上也更方便一些Github:[](https://github.com/alibaba/ea...java
然而我在使用的時候發現仍是有不少坑,其中一個比較頭疼的是對單個單元格樣式的設置。EasyExcel提供了一個BaseRowModel做爲每行數據的一個模型,而且其中有一個屬性cellStyleMap
表明每列樣式的集合,原本我覺得這個只要在本身定義模型的時候,也把CellStyle
定義進去就好了,然而,仍是我想多了……定義了CellStyle
並無什麼卵用,這是第一個蛋疼的地方git
/** * 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的方法,不知道什麼緣由讓阿里巴巴不提供這樣一個接口……這是第二個蛋疼的地方github
既然沒有提供接口,那就只能用反射來硬剛了,下面就直接上代碼了apache
我這裏是在開始寫數據以前就將每張表的CellStyle
與每張表關聯起來,再在後面的handler中獲取到這個CellStyle
進行設置xss
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設計的也很蛋疼),並注入咱們本身的業務邏輯(設置單元格樣式)。ide
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("已簽到")); } } } }
上面有一些簡單的邏輯處理,就不一一介紹了。測試
若文章有任何問題,歡迎留言指出——做者博客:桔子筆記ui