问题:
在Java读取Excel导入操作时, sheet.getLastRowNum()会统计带有格式的空白行,导致获取的最大行数不准确,数据导入失败。
解决:
在操作过程中删除存在样式的空白行
可通过将空白行(内容为空,但是存在样式)进行sheet.removeRow删除后
再调用getLastRowNum()进行行数统计,getAccuracyContextNum()方法中。
@Transactional(rollbackFor = Exception.class)
public Map<String,Object> importWeb(Workbook wb,String skillName,String skilltype,String office) {
StringBuilder msg = new StringBuilder();
Map<String, Object> reMap = new HashMap<>();
Sheet sheet = wb.getSheetAt(0);
Row titleRow = sheet.getRow(0);
if (titleRow.getLastCellNum() != 3 || !"项目名称".equals(getCellValue(titleRow.getCell(0)))
|| !"操作要点".equals(getCellValue(titleRow.getCell(1))) || !"总分".equals(getCellValue(titleRow.getCell(2)))){
reMap.put("flag",false);
reMap.put("msg","模板错误");
return reMap;
}
//获取合并单元格信息的hashmap
Map<String,Integer[]> mergedRegionMap = getMergedRegionMap(sheet);
//拿到excel的最后一行的索引
// int lastRowNum = sheet.getLastRowNum();
sheet=getAccuracyContextNum(wb,mergedRegionMap);
int lastRowNum=sheet.getLastRowNum();
List<Map<String, Object>> projectNameList = new ArrayList<>();
List<Map<String, Object>> mainPointsList = new ArrayList<>();
//nursetrain_skill_oper表主键id
String operId = IdGen.uuid();
BigDecimal totalScore = new BigDecimal(0);
//从excel的第二行索行开始,遍历到最后一行(第一行是标题,直接跳过不读取)
for(int i = 1; i<=lastRowNum ; i++) {
//拿到excel的行对象
Row row = sheet.getRow(i);
//获取excel的行中有多个列
int cellNum = row.getLastCellNum();
if (cellNum > 3){
reMap.put("flag",false);
reMap.put("msg","数据列超出标题范围");
return reMap;
}
Map<String, Object> projectMap = new LinkedHashMap<>();
Map<String, Object> mainPointsMap = new LinkedHashMap<>();
//对每行进行列遍历,即一列一列的进行解析
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 = sheet.getRow(firstRowNumberAndCellNumber[0]);
Cell cellTmp = rowTmp.getCell(firstRowNumberAndCellNumber[1]);
if (j == 0 ){
projectMap.put("NAME", getCellValue(cellTmp));
if(StringUtils.isEmpty(getCellValue(cellTmp))) {
msg.append("第").append(i + 1).append("行项目名称不能为空").append("<br>");
}
}
if (j == 1){
mainPointsMap.put("NAME", getCellValue(cellTmp));
if(StringUtils.isEmpty(getCellValue(cellTmp))) {
msg.append("第").append(i + 1).append("行操作要点不能为空").append("<br>");
}
}
if (j == 2){
mainPointsMap.put("SCORE", getCellValue(cellTmp));
if(StringUtils.isEmpty(getCellValue(cellTmp))) {
msg.append("第").append(i + 1).append("行总分不能为空").append("<br>");
}else{
try{
BigDecimal score = new BigDecimal(getCellValue(cellTmp)).setScale(2,RoundingMode.DOWN);
totalScore = totalScore.add(score);
}catch (Exception e){
msg.append("第").append(i + 1).append("行总分只能为数字").append("<br>");
}
}
}
mainPointsMap.put("order", i);
}else{
if (j == 0 ){
projectMap.put("NAME", getCellValue(cell));
if(StringUtils.isEmpty(getCellValue(cell))) {
msg.append("第").append(i + 1).append("行项目名称不能为空").append("<br>");
}
}
if (j == 1){
mainPointsMap.put("NAME", getCellValue(cell));
if(StringUtils.isEmpty(getCellValue(cell))) {
msg.append("第").append(i + 1).append("行操作要点不能为空").append("<br>");
}
}
if (j == 2){
mainPointsMap.put("SCORE", getCellValue(cell));
if(StringUtils.isEmpty(getCellValue(cell))) {
msg.append("第").append(i + 1).append("行总分不能为空").append("<br>");
}else {
try{
BigDecimal score = new BigDecimal(String.valueOf(getCellValue(cell))).setScale(2,RoundingMode.UP);
totalScore = totalScore.add(score);
}catch (Exception e){
msg.append("第").append(i + 1).append("行总分只能为数字").append("<br>");
}
}
}
mainPointsMap.put("order", i);
}
}
``````
try {
//插入nursetrain_skill_oper表
//插入nursetrain_skill_oper_item表
//插入nursetrain_skill_oper_item_step表
reMap.put("flag",true);
reMap.put("msg","导入成功");
}catch (Exception e){
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
reMap.put("flag",false);
reMap.put("msg","导入失败,请联系系统管理员");
}
return reMap;
}
//获取准确的文件行数
public Sheet getAccuracyContextNum(Workbook workbook,Map<String,Integer[]> mergedRegionMap) {
// 取第一个sheet
Sheet sheet = workbook.getSheetAt(0);
// 删除空行
for (int i = 0; i <= sheet.getLastRowNum(); i++) {
Row row = sheet.getRow(i);
int cellNum = row.getLastCellNum();
boolean flag=false;
for(int j=0; j < cellNum; j++) {
Integer[] firstRowNumberAndCellNumber = mergedRegionMap.get(i+"_"+j);
if(firstRowNumberAndCellNumber != null){
flag=true;
}
}
if (this.isRowEmpty(row) && !flag) {
// 删除空行
if (this.isRowEmpty(row)) {
int lastRowNum = sheet.getLastRowNum();
if (i >= 0 && i < lastRowNum) {
sheet.shiftRows(i + 1, lastRowNum, -1);// 将行号为i+1一直到行号为lastRowNum的单元格全部上移一行,以便删除i行
}
if (i == lastRowNum) {
if (row != null) {
sheet.removeRow(row);
}
}
i--;
}
}
return sheet;
}
/**
* 判断是否有空行
* @param row
* @return
*/
private boolean isRowEmpty(Row row) {
for (int c = row.getFirstCellNum(); c < row.getLastCellNum(); c++) {
Cell cell = row.getCell(c);
if (cell != null && cell.getCellType() != Cell.CELL_TYPE_BLANK) {
return false;
}
}
return true;
}
//将存在合并单元格的列记录入put进hashmap并返回
public 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;
}