赞
踩
目录
3. EasyExcel读取Excel文件并对文件表头进行校验
4. EasyExcel写入Excel文件并根据文件内容自适应宽高
EasyExcel是阿里巴巴开源的 Excel 读取框架,其最大的特点是极低的内存消耗和极高的文件读取速度。在本文中使用 EasyExcel,实现了读取 Excel 文件并对文件表头进行校验,写入 Excel文件并根据写入内容自适应调整单元格宽高。
其他更详细的信息,可以查阅官网:EasyExcel官方文档
在开始编码之前,需要先引入 EasyExcel、lombok 和 fastjson 依赖。
- <dependency>
- <groupId>com.alibaba</groupId>
- <artifactId>easyexcel</artifactId>
- <version>3.1.0</version>
- </dependency>
- <dependency>
- <groupId>org.projectlombok</groupId>
- <artifactId>lombok</artifactId>
- <version>1.18.8</version>
- </dependency>
- <dependency>
- <groupId>com.alibaba</groupId>
- <artifactId>fastjson</artifactId>
- <version>1.2.60</version>
- </dependency>
需要读取的 Excel 文件如下:
定义与 Excel 文件相对应的实体类。lombok 的 @Data 注解会为实体类自动生成 get 、set 以及 toString 方法。EasyExcel 的 @ExcelProperty 注解,定义了与 javaBean 字段相对应的 Excel 字段。
- @Data
- public class ExcelDTO {
- @ExcelProperty("姓名")
- private String name;
- @ExcelProperty("年龄")
- private int age;
- @ExcelProperty("分数")
- private Double score;
- }
代码实现读取 Excel 文件,读取的数据被封装进 List<ExcelDTO> 中。
- @Test
- public void readExcel() {
- File file = new File("src/main/resources/ExcelDemo.xlsx");
- //默认读取Excel文件的第一个sheet
- List<ExcelDTO> list = EasyExcel.read(file).head(ExcelDTO.class).sheet().doReadSync();
- for (ExcelDTO data : list) {
- System.out.println(data);
- }
- }
如果想实现在读取Excel文件的同时,对表头和文件内容进行校验,则需要定义一个 Listener 实现 EasyExcel 提供的 ReadListener 接口,实现代码如下。
- import com.alibaba.excel.context.AnalysisContext;
- import com.alibaba.excel.event.AnalysisEventListener;
- import java.util.Map;
-
- public class DemoDataListener extends AnalysisEventListener<ExcelDTO> {
- /**
- * 每解析一条数据,都会来调用该方法
- * 对所有数据进行校验,在此增加校验逻辑
- *
- * @param excelDTO
- * @param analysisContext
- */
- @Override
- public void invoke(ExcelDTO excelDTO, AnalysisContext analysisContext) {
- }
-
- /**
- * 每解析一行表头,会调用该方法
- *
- * @param headMap
- * @param context
- */
- @Override
- public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
- if (!headMap.containsKey(0) || !headMap.containsKey(1) || !headMap.containsKey(2)
- || !headMap.get(0).equals("姓名") || !headMap.get(1).equals("年龄")
- || !headMap.get(2).equals("分数")) {
- throw new RuntimeException("表头校验失败");
- }
- }
-
- /**
- * 所有数据都解析完成后,会调用该方法
- *
- * @param analysisContext
- */
- @Override
- public void doAfterAllAnalysed(AnalysisContext analysisContext) {
- }
- }
同时,在读取Excel文件时,也需要加入 Listener ,实现的代码如下:
- @Test
- public void readExcel() {
- File file = new File("src/main/resources/ExcelDemo.xlsx");
- DemoDataListener listener = new DemoDataListener();
- List<ExcelDTO> list = new ArrayList<>();
- try {
- //默认读取Excel文件的第一个sheet
- list = EasyExcel.read(file, listener).head(ExcelDTO.class).sheet().doReadSync();
- } catch (Exception e) {
- //此处可修改为warn级别日志打印
- System.out.println("表头校验失败");
- }
-
- for (ExcelDTO data : list) {
- System.out.println(data);
- }
- }
首先初始化一个 List<ExcelDTO> ,将 name 字段的内容,设置的长一些,通过如下代码可以将数据写入到 Excel 表格。
- @Test
- public void writeExcel() {
- List<ExcelDTO> list = new ArrayList<>();
- for (int i = 0; i < 10; i++) {
- ExcelDTO excelDTO = new ExcelDTO();
- excelDTO.setName("名字名字名字名字名字名字名字" + i);
- excelDTO.setAge(i + 30);
- excelDTO.setScore((double) (60 + i));
- list.add(excelDTO);
- }
- File file = new File("src/main/resources/WriteDemo.xlsx");
- EasyExcel.write("demo1.xlsx", ExcelDTO.class).sheet("模板").doWrite(list);
- }
写入 Excel 文件的内容如下所示,可以看到姓名字段因为内容过长,而被隐藏了一部分,查看 Excel 文件时不便捷。
这时,自定义一个 Config 类继承自 EasyExcel 的 AbstractColumnWidthStyleStrategy 类,达到根据内容自适应宽度的效果。
- import com.alibaba.excel.enums.CellDataTypeEnum;
- import com.alibaba.excel.metadata.Head;
- import com.alibaba.excel.metadata.data.CellData;
- import com.alibaba.excel.metadata.data.WriteCellData;
- import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
- import com.alibaba.excel.write.style.column.AbstractColumnWidthStyleStrategy;
- import org.apache.commons.collections4.CollectionUtils;
- import org.apache.poi.ss.usermodel.Cell;
- import org.apache.poi.ss.usermodel.Sheet;
- import java.util.HashMap;
- import java.util.List;
- import java.util.Map;
-
- public class CustomCellWriteWeightConfig extends AbstractColumnWidthStyleStrategy {
- private final Map<Integer, Map<Integer, Integer>> CACHE = new HashMap<>();
-
- @Override
- protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List<WriteCellData<?>> cellDataList, Cell cell, Head head, Integer integer, Boolean isHead) {
- boolean needSetWidth = isHead || !CollectionUtils.isEmpty(cellDataList);
- if (needSetWidth) {
- //computeIfAbsent方法的作用为,在通过key获取value时,若key对应的value存在,则返回value值;若不存在,则创建一个符合条件的value并返回
- Map<Integer, Integer> maxColumnWidthMap = CACHE.computeIfAbsent(writeSheetHolder.getSheetNo(), k -> new HashMap<>());
-
- Integer columnWidth = this.dataLength(cellDataList, cell, isHead);
- if (columnWidth >= 0) {
- if (columnWidth > 254) {
- columnWidth = 254;
- }
- Integer maxColumnWidth = maxColumnWidthMap.get(cell.getColumnIndex());
- //需要将列宽,设置为当前列最长内容的长度
- if (maxColumnWidth == null || columnWidth > maxColumnWidth) {
- maxColumnWidthMap.put(cell.getColumnIndex(), columnWidth);
- Sheet sheet = writeSheetHolder.getSheet();
- sheet.setColumnWidth(cell.getColumnIndex(), columnWidth * 256);
- }
- }
- }
- }
-
- /**
- * 计算当前内容的长度,(实际就是获取当前内容的字节长度)
- *
- * @param cellDataList
- * @param cell
- * @param isHead
- * @return
- */
- private Integer dataLength(List<WriteCellData<?>> cellDataList, Cell cell, Boolean isHead) {
- if (isHead) {
- return cell.getStringCellValue().getBytes().length;
- } else {
- CellData<?> cellData = cellDataList.get(0);
- CellDataTypeEnum type = cellData.getType();
- if (type == null) {
- return -1;
- } else {
- switch (type) {
- case STRING:
- // 若存在换行符,则需要根据换行符的位置进行针对性处理
- int index = cellData.getStringValue().indexOf("\n");
- return index != -1 ?
- cellData.getStringValue().substring(0, index).getBytes().length + 1 : cellData.getStringValue().getBytes().length + 1;
- case BOOLEAN:
- return cellData.getBooleanValue().toString().getBytes().length;
- case NUMBER:
- return cellData.getNumberValue().toString().getBytes().length;
- default:
- return -1;
- }
- }
- }
- }
- }
同样的,自定义一个 Config 类继承自 EasyExcel 的 AbstractRowHeightStyleStrategy 类,达到自适应高度的效果。
- import com.alibaba.excel.write.style.row.AbstractRowHeightStyleStrategy;
- import org.apache.poi.ss.usermodel.Cell;
- import org.apache.poi.ss.usermodel.CellType;
- import org.apache.poi.ss.usermodel.Row;
- import java.util.Iterator;
- import java.util.Objects;
-
- public class CustomCellWriteHeightConfig extends AbstractRowHeightStyleStrategy {
- /**
- * 默认高度
- */
- private static final Integer DEFAULT_HEIGHT = 300;
-
- /**
- * 设置表头的高度
- *
- * @param row
- * @param relativeRowIndex
- */
- @Override
- protected void setHeadColumnHeight(Row row, int relativeRowIndex) {
- }
-
- /**
- * 设置内容的高度
- *
- * @param row
- * @param relativeRowIndex
- */
- @Override
- protected void setContentColumnHeight(Row row, int relativeRowIndex) {
- Iterator<Cell> cellIterator = row.cellIterator();
- if (!cellIterator.hasNext()) {
- return;
- }
- // 默认为 1行高度
- int maxHeight = 1;
- while (cellIterator.hasNext()) {
- Cell cell = cellIterator.next();
- if (Objects.requireNonNull(cell.getCellTypeEnum()) == CellType.STRING) {
- if (cell.getStringCellValue().contains("\n")) {
- int length = cell.getStringCellValue().split("\n").length;
- maxHeight = Math.max(maxHeight, length);
- }
- }
- }
- row.setHeight((short) (maxHeight * DEFAULT_HEIGHT));
- }
- }
在写入 Excel 文件时,将自定义的两个 Config 类注册到 EasyExcel 中。
- @Test
- public void writeExcel() {
- List<ExcelDTO> list = new ArrayList<>();
- for (int i = 0; i < 10; i++) {
- ExcelDTO excelDTO = new ExcelDTO();
- excelDTO.setName("名字名字名字名字名字名字名字" + i);
- excelDTO.setAge(i + 30);
- excelDTO.setScore((double) (60 + i));
- list.add(excelDTO);
- }
- File file = new File("src/main/resources/WriteDemo.xlsx");
- EasyExcel.write("demo1.xlsx", ExcelDTO.class).sheet("模板").
- registerWriteHandler(new CustomCellWriteWeightConfig()).registerWriteHandler(new CustomCellWriteHeightConfig()).doWrite(list);
- }
写入的 Excel 文件如图,可以看到宽度已经经过自适应调整了。
关注公众号打代码的苏比特,获取java面试百问百答和面试高频算法题~
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。