解析合并单元格并拆解
前言:废话少说,先看效果再上代码
一. 合并模版
二. 代码
1.依赖
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-spring-boot-starter</artifactId>
<version>4.4.0</version>
<exclusions>
<exclusion>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-wps</artifactId>
</exclusion>
</exclusions>
</dependency>
2.代码
@Data
public class Student implements IExcelModel,
IExcelDataModel {
private static final long serialVersionUID = 1L;
// 年级
private String gradeName;
// 班级
@Excel(name = "班级", orderNum = "1", width = 40)
private String className;
@Excel(name = "姓名", orderNum = "2", width = 40)
@NotNull(message = "不能为空")
@Size(mess![在这里插入图片描述](https://img-blog.csdnimg.cn/cef3ddc111a7442481f44d0f856a92cf.png#pic_center)
age = "不能超过10个字符", max = 10)
private String name;
@Excel(name = "性别", orderNum = "3", width = 40, replace = {"男_1", "女_0"})
@NotNull(message = "不能为空")
private String sex;
@Excel(name = "年龄", orderNum = "4", width = 40)
@NotNull(message = "不能为空")
private String age;
private String errorMsg;
private Integer rowNum;
@Override
public String getErrorMsg() {
return errorMsg;
}
@Override
public void setErrorMsg(String errorMsg) {
this.errorMsg = errorMsg;
}
@Override
public Integer getRowNum() {
return rowNum;
}
@Override
public void setRowNum(Integer rowNum) {
this.rowNum = rowNum;
}
}
@Override
public void importExcel(MultipartFile file, HttpServletRequest request) throws Exception {
// 1.获取workBook
Workbook workbook = ExcelUtil.getWorkbook(file);
// 2.获取sheet页 TODO 默认单页导入 多页导入下集分享 敬请期待
Sheet sheetAt0 = workbook.getSheetAt(0);
// 3.拆解合并单元格 拆解后就相当于是普通的excel导入了
decomposeMergedCell(sheetAt0);
// 4.使用EasyPOI内的ExcelUtil工具类根据Entity.class内@Excel注解与导入文件匹配
// 从而导入数据
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
workbook.write(outputStream);
ByteArrayInputStream inputStream = new ByteArrayInputStream(
outputStream.toByteArray());
// 导入文件配置
ImportParams params = new ImportParams();
//表格标题行数,默认0 表示没有标题
params.setTitleRows(0);
//表头行数,默认1 表示有一行表头
params.setHeadRows(1);
//开始读取的sheet位置,默认为0
params.setStartSheetIndex(0);
//需要校验
params.setNeedVerify(true);
ExcelImportResult<Student> importResult = ExcelImportUtil.importExcelMore(inputStream,
Student.class, params);
// 5.第三步可使纵向合并拆解第四部成功匹配到属性上,但是还有横向合并单元格需要特殊处理一下
// 因年级占单独一行且在头一行 导入后把校验通过与不通过的放一起重新排序,
// 再根据相同类型字段值全部一致判断该行数据是年级名
List<Student> list = importResult.getList();
list.addAll(importResult.getFailList());
specialDispose(list);
// 重新分组
list = list.stream()
.filter(pomVo -> StringUtils.isBlank(pomVo.getErrorMsg())).collect(Collectors.toList());
// 解析完成
list.forEach(System.out::println);
System.out.println("SOCCESS");
}
private void specialDispose(List<Student> list) {
String gradeName = "";
Iterator<Student> iterator = list.iterator();
while (iterator.hasNext()) {
Student student = iterator.next();
student.setGradeName(gradeName);
// 年级名称
if (null != student.getClassName() &&
student.getClassName().equals(student.getName())) {
gradeName = student.getClassName();
iterator.remove();
}
}
public void decomposeMergedCell(Sheet sheetAt0) {
//获取合并单元格信息的hashmap
Map<String, Integer[]> mergedRegionMap = ExcelUtil.getMergedRegionMap(sheetAt0);
//拿到excel的最后一行的索引
int lastRowNum = sheetAt0.getLastRowNum();
//从excel的第二行索行开始,遍历到最后一行(第一行是标题,直接跳过不读取)
for (int i = 0; i <= lastRowNum; i++) {
//拿到excel的行对象
Row row = sheetAt0.getRow(i);
//获取excel的行中有多个列
int cellNum = row.getLastCellNum();
//对每行进行列遍历,即一列一列的进行解析
for (int j = 0; j < cellNum; j++) {
//拿到了excel的列对象
Cell cell = row.getCell(j);
//将列对象的行号和列号+下划线组成key去hashmap中查询,不为空说明当前的cell是合并单元列
Integer[] firstRowNumberAndCellNumber = mergedRegionMap.get(i + "_" + j);
//如果是合并单元列,就取合并单元格的首行和首列所在位置读数据,否则就是直接读数据
if (firstRowNumberAndCellNumber != null) {
Row rowTmp = sheetAt0.getRow(firstRowNumberAndCellNumber[0]);
Cell cellTmp = rowTmp.getCell(firstRowNumberAndCellNumber[1]);
setCellValue(cellTmp, cell);
}
}
}
}
public String setCellValue(Cell cellTmp, Cell cell) {
if (cellTmp == null) {
return "";
}
if (cellTmp.getCellType() == CellType.STRING) {
cell.setCellValue(cellTmp.getStringCellValue());
return cellTmp.getStringCellValue();
} else if (cellTmp.getCellType() == CellType.BOOLEAN) {
cell.setCellValue(cellTmp.getBooleanCellValue());
return String.valueOf(cellTmp.getBooleanCellValue());
} else if (cellTmp.getCellType() == CellType.FORMULA) {
cell.setCellValue(cellTmp.getCellFormula());
return cellTmp.getCellFormula();
} else if (cellTmp.getCellType() == CellType.NUMERIC) {
cell.setCellValue(cellTmp.getNumericCellValue());
return String.valueOf(cellTmp.getNumericCellValue());
}
return "";
}
//将存在合并单元格的列记录入put进hashmap并返回
public static Map<String,Integer[]> getMergedRegionMap(Sheet sheet){
Map<String,Integer[]> result = new HashMap<String,Integer[]>();
//获取excel中的所有合并单元格信息
int sheetMergeCount = sheet.getNumMergedRegions();
//遍历处理
for (int i = 0; i < sheetMergeCount; i++) {
//拿到每个合并单元格,开始行,结束行,开始列,结束列
CellRangeAddress range = sheet.getMergedRegion(i);
int firstColumn = range.getFirstColumn();
int lastColumn = range.getLastColumn();
int firstRow = range.getFirstRow();
int lastRow = range.getLastRow();
//构造一个开始行和开始列组成的数组
Integer[] firstRowNumberAndCellNumber = new Integer[]{firstRow,firstColumn};
//遍历,将单元格中的所有行和所有列处理成由行号和下划线和列号组成的key,然后放在hashmap中
for(int currentRowNumber = firstRow; currentRowNumber <= lastRow; currentRowNumber++) {
for(int currentCellNumber = firstColumn; currentCellNumber <= lastColumn; currentCellNumber ++) {
result.put(currentRowNumber+"_"+currentCellNumber, firstRowNumberAndCellNumber);
}
}
}
return result;
}
三.总结
1.本次分享是先解析拆解到workbook在进行导入.
2.横列合并单元格个人是没有好的方法进行拆分只能按照业务逻辑灵活处理.
3.重在记录,欢迎评价、指正.