POI导入数据解决多表头,合并单元格,适配表格列可拓展数据读取问题
关于java开发,涉及使用POI读取excel数据,尤其涉及多分类的表头以及合并单元格的导入会遇到很多困扰的问题,今天我就将在开发中遇到的导入问题进行讲解。
1.首先说下我们今天要实现的功能,我们要将带有多分类,多合并单元格进行数据转存,以及需要适配不同列的数据解析导入,具体的表格如下:
表格中圈出的区域为:
(1)第一行为:合并单元格主标题
(2)第二行为:合并的性别类
(3)第三行为:产品分类
(4)第四行为:产品子级分类
上述的罗列的几个都是以合并单元格的形式进行展示,首先我们要解决的就是如何进行合并单元格的数据读取
第一步:pom文件引入关于poi相应的文件包,具体如图所示:
第二步:我们需要解决的就是关于合并单元格的数据读取,获取表格中合并单元格的数量,判断是否是单元格,以及获取合并单元格的跨列数。通过以上几点的思路我们来进行逻辑的实现,首先步入代码:
获取单元格合并列数:
public static int GetMergeNum(Cell cell, Sheet sheet) {
int mergeSize = 1;
List<CellRangeAddress> mergedRegions = sheet.getMergedRegions();
for (CellRangeAddress cellRangeAddress : mergedRegions) {
if (cellRangeAddress.isInRange(cell)) {
mergeSize = cellRangeAddress.getFirstRow()-cellRangeAddress.getLastRow()+1;//获取合并的列数
break;
}
}
return mergeSize;
}
判断是否是单元格方法
public boolean isMergedRegion(Sheet sheet,int row ,int column) {
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();
if(row >= firstRow && row <= lastRow){
if(column >= firstColumn && column <= lastColumn){
return true;
}
}
}
return false;
}
获取合并单元格的值方法
public String getMergedRegionValue(Sheet sheet ,int row , int column){
int sheetMergeCount = sheet.getNumMergedRegions();
for(int i = 0 ; i < sheetMergeCount ; i++){
CellRangeAddress ca = sheet.getMergedRegion(i);
int firstColumn = ca.getFirstColumn();
int lastColumn = ca.getLastColumn();
int firstRow = ca.getFirstRow();
int lastRow = ca.getLastRow();
if(row >= firstRow && row <= lastRow){
if(column >= firstColumn && column <= lastColumn){
Row fRow = sheet.getRow(firstRow);
Cell fCell = fRow.getCell(firstColumn);
return getCellValue(fCell) ;
}
}
}
return null ;
}
public String getCellValue(Cell cell){
if(cell == null) return "";
return cell.getStringCellValue();
}
上述的几个属于实现功能的基本代码,现在我们整体来看下业务实现流程的完成代码:
public synchronized ResultMap rateImportExcel(HttpServletRequest request, HttpServletResponse response,Integer type) {
String msg="";
try {
// @RequestParam("file") MultipartFile file 是用来接收前端传递过来的文件
// 1.创建workbook对象,读取整个文档
// 转类型接受file
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
MultipartFile file = multipartRequest.getFile("excelFile");
// 获取登陆账户信息
// 获取携带的参数
InputStream inputStream = file.getInputStream();
Workbook wb = null;
if (file.getOriginalFilename().indexOf(".xlsx") == -1) {
wb = new HSSFWorkbook(inputStream);
}else {
wb = new XSSFWorkbook(inputStream);
}
// 2.读取页脚sheet
Sheet sheetAt = wb.getSheetAt(0);
String projectName="";//项目名称
if(isMergedRegion(sheetAt,0 ,1)==true) {
projectName = getMergedRegionValue(sheetAt ,0 , 1);
}
InsuranceProdect insuranceProdectInfo = insuranceProdectService.selectProdectInfoByName(projectName);
if(insuranceProdectInfo==null) {
msg="系统内未找到对应产品信息,请检查!";
}
//编辑表格中的合并单元格的数量
int sheetmergeCount = sheetAt.getNumMergedRegions();
int maleFirstColumn = 0;//男性初始列
int maleLastColumn = 0;//男性的结束列
int girlFirstColumn = 0;//女性初始列
int girlLastColumn = 0;//女性结束列
int maleParentFirstColumn = 0;//男性父级分类初始列
int maleParentLastColumn = 0;//男性父级分类结束列
int maleParent1FirstColumn = 0;//男性父级分类2初始列
int maleParent1LastColumn = 0;//男性父级分类2结束列
int girlParentFirstColumn = 0;//女性父级分类初始列
int grilParentLastColumn = 0;//女性父级分类结束列
int girlParent1FirstColumn = 0;//女性父级分类2初始列
int girlParent1LastColumn = 0;//女性父级分类2结束列
//遍历合并单元格数量获取对应的初始列数,结束列数后续使用
for (int i = 1; i <sheetmergeCount; i++) {
CellRangeAddress range = sheetAt.getMergedRegion(i);
if(i==1) {
maleFirstColumn = range.getFirstColumn();
maleLastColumn = range.getLastColumn();
}else if(i==2) {
girlFirstColumn = range.getFirstColumn();
girlLastColumn = range.getLastColumn();
}else if(i==3) {
maleParentFirstColumn = range.getFirstColumn();
maleParentLastColumn = range.getLastColumn();
}else if(i==4) {
maleParent1FirstColumn = range.getFirstColumn();
maleParent1LastColumn = range.getLastColumn();
}else if(i==5) {
girlParentFirstColumn = range.getFirstColumn();
grilParentLastColumn = range.getLastColumn();
}else if(i==6) {
girlParent1FirstColumn = range.getFirstColumn();
girlParent1LastColumn = range.getLastColumn();
}
}
//获取二级分类的集合值
List<String> typeList = new ArrayList<>();
for(int i=maleFirstColumn+1;i<=maleLastColumn;i++) {
Row rowInfo = sheetAt.getRow(3);//有二级分类
typeList.add(rowInfo.getCell(i).getStringCellValue().trim());
}
String male = "";//男性信息
if(isMergedRegion(sheetAt ,1,maleLastColumn)==true) {
male = getMergedRegionValue(sheetAt ,1 , maleLastColumn);
}
String femaleSex = "";//女性信息
if(isMergedRegion(sheetAt,1,girlLastColumn)==true) {
femaleSex = getMergedRegionValue(sheetAt,1,girlLastColumn);
}
String parentName = "";//父级一信息
String parent1Name = "";//父级二信息
if(type==2) {
if(isMergedRegion(sheetAt ,1,maleLastColumn)==true) {
parentName = getMergedRegionValue(sheetAt ,2 , maleParentLastColumn);
}
if(isMergedRegion(sheetAt,1,girlLastColumn)==true) {
parent1Name = getMergedRegionValue(sheetAt,2,maleParent1LastColumn);
}
}
// 3.循环读取某一行
int index=3;
int rowNum = sheetAt.getLastRowNum();//有多少行
for (int i = index; i <= rowNum; i++) {
Row row = sheetAt.getRow(i);//第i行
if (row == null) {//过滤空行
continue;
}
// 4.读取每一行的单元格
if (index ==3) {
index++;
continue;
} else {
index++;
}
//初始化错误信息
String age = "";//年龄
try {
if (row.getCell(0) != null) {
row.getCell(0).setCellType(CellType.STRING);
age = row.getCell(0) == null ? "" : row.getCell(0).getStringCellValue().trim(); // 第一列数据
} else {
// 如果没有数据了 就跳出
break;
}
} catch (Exception e) {
}
//根据男性的父级分类跨列数进行编辑循环,插入数据
for(int k=maleParentFirstColumn+1;k<=maleParentLastColumn;k++) {
String maleValue="";
if(row.getCell(k)!=null) {
row.getCell(k).setCellType(CellType.STRING);
maleValue = row.getCell(k) == null ? "" : row.getCell(k).getStringCellValue().trim();//第一列数据
RateTypeEx info = rateTypeService.selectRateTypeInfoByName(typeList.get(k-1));
RateTypeEx parentInfo = rateTypeService.selectRateTypeInfoByName(parentName);
if(info==null) {
msg="系统内未找到对应分类信息,请检查!";
}else {
if(Utils.notBlank(maleValue)) {
//业务保存方法
saveRate(insuranceProdectInfo.getId(),parentInfo.getId(),info.getId(),2,maleValue,age,male);
}
}
}
}
//根据男性的父级分类2跨列数进行编辑循环,插入数据
for(int k=maleParent1FirstColumn;k<=maleParent1LastColumn;k++) {
String maleValue="";
if(row.getCell(k)!=null) {
row.getCell(k).setCellType(CellType.STRING);
maleValue = row.getCell(k) == null ? "" : row.getCell(k).getStringCellValue().trim();//第一列数据
RateTypeEx info = rateTypeService.selectRateTypeInfoByName(typeList.get(k-1));
RateTypeEx parentInfo1 = rateTypeService.selectRateTypeInfoByName(parent1Name);
if(info==null) {
msg="系统内未找到对应分类信息,请检查!";
}else {
if(Utils.notBlank(maleValue)) {
//业务保存方法
saveRate(insuranceProdectInfo.getId(),parentInfo1.getId(),info.getId(),2,maleValue,age,male);
}
}
}
}
//根据女性的父级分类跨列数进行编辑循环,插入数据
for(int k=girlParentFirstColumn;k<=grilParentLastColumn;k++) {
String girlValue="";
if(row.getCell(k)!=null) {
row.getCell(k).setCellType(CellType.STRING);
girlValue = row.getCell(k) == null ? "" : row.getCell(k).getStringCellValue().trim();//第一列数据
RateTypeEx info = rateTypeService.selectRateTypeInfoByName(typeList.get(k-5));
RateTypeEx parentInfo = rateTypeService.selectRateTypeInfoByName(parentName);
if(info==null) {
msg="系统内未找到对应分类信息,请检查!";
}else {
if(Utils.notBlank(girlValue)) {
//业务保存方法
saveRate(insuranceProdectInfo.getId(),parentInfo.getId(),info.getId(),2,girlValue,age,femaleSex);
}
}
}
}
//根据女性的父级分类2跨列数进行编辑循环,插入数据
for(int k=girlParent1FirstColumn;k<=girlParent1LastColumn;k++) {
String girlValue="";
if(row.getCell(k)!=null) {
row.getCell(k).setCellType(CellType.STRING);
girlValue = row.getCell(k) == null ? "" : row.getCell(k).getStringCellValue().trim();//第一列数据
RateTypeEx info = rateTypeService.selectRateTypeInfoByName(typeList.get(k-7));
RateTypeEx parentInfo1 = rateTypeService.selectRateTypeInfoByName(parent1Name);
if(info==null) {
msg="系统内未找到对应分类信息,请检查!";
}else {
if(Utils.notBlank(girlValue)) {
//业务保存方法
saveRate(insuranceProdectInfo.getId(),parentInfo1.getId(),info.getId(),2,girlValue,age,femaleSex);
}
}
}
}
}
}catch (Exception e) {
e.printStackTrace();
if (StringUtils.isBlank(msg)) {
msg +=msg;
}
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
return ResultMap.error(msg);
}
return ResultMap.success();
}
以上就是大概的具体实现步骤,在开发过程中要思路明确,当遇到这种涉及读取跨列合并单元格的表格时,我们要掌握一开始上述所说的最基本的读取单元格,获取跨列数等系列方法,并且结合自己的业务需求进行处理即可,好了,今天就到这边了,希望大家给我支持,后续继续进行遇坑分享!