Excel 文件数据导入数据库 |
首先说明下读写Excel三种常用的技术:
1. POI : 效率高,操作相对JXL复杂,支持公式,宏,图像图表,一些企业应用上会比较实用.能够修饰单元格属性.字体 数字 日期操作.
2. FASTEXCEL: 不可操作样式,推荐简单的导入,速度快.
3. JXL : 效率低,简单.部分支持.能够修饰单元格属性,但没有POI强大.同样支持字体 数字 日期操作.
本次演示的是使用POI进行导入
为了快速搭建使用SpringBoot,先导入相关Jar包.
<!-- POI -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.8</version>
<exclusions>
<exclusion>
<artifactId>commons-codec</artifactId>
<groupId>commons-codec</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.8</version>
</dependency>
.....省略相关数据库驱动.
大体流程 :
1.前端页面文件提交按钮,把需要导入的Excel文件提交上去.
2.后端处理器接收后对文件进行读取,并分析信息.然后把文件中的每一行封装为一个对象,添加进入list集合中.
3.(可省略)把封装好的list集合存入缓存中(Redis),把提取出来的数据相应给前端页面,给操作者检查.其中数据包含分析数据,提示哪里可能出现遗漏数据.当操作者缺认后,点击添加,则将缓存中数据取出对应存入数据库中.
最关键的一步还是在提取数据环节(这里可能会遇到空指针异常 , 就需要处理.),需要保证精确的提取出数据,防止遗漏.
上代码(关键部分代: 提取数据)
/**
* 处理Excel文件数据,打包成List对象集合
* @param file Excel数据文件
* @return
*/
public static (打包对象) readExcel(MultipartFile file){
//============准备工作,相关参数,也可创建一个配置类,来封装一下参数.
int page = 页数; //页数
int col = 列数; //列
int rows = 0; //行数
String msg = "非常流畅,无失误!"; //错误消息
int num = 0; //实际处理多少条数据
int num1= 0; //完整无跳转处理多少页
int num2= 0; //处理到多少页
List<String> erNum = new ArrayList<>(); //记录哪个点进行跳转,触发自动跳转次数
List<Shop> list = new ArrayList<>(); //封装后的对象集合
实体对象 object = null; // 根据Excel数据创建的实体类
long endTime = 0; //结束时间
long startTime = 0; //开始时间
try{
log.info("即将处理文件:{}",file.getOriginalFilename());
InputStream is = file.getInputStream(); //获取文件流信息
Workbook wb = new XSSFWorkbook(is);
Sheet sheet = null; //页对象
startTime = System.currentTimeMillis(); //计时开始(性能检测)
//======开始读取===========
for(int k = 0; k < page; k++){ //循环每一页
sheet = wb.getSheetAt(k);
//rows = sheet.getPhysicalNumberOfRows()-1; //获取物理行数-1 减去标题一栏
rows = sheet.getLastRowNum(); //最后的行号
System.out.println("当前页数:"+ (k+1) +" 本页行数:"+ (rows-1));
num2++;
//一下两行代码业务需要可添加: 比如可能有跨行的列,多行使用同一个列的情况下,举个例子:广东省下深圳,佛山.....
String temp = "暂无"; //临时属性变量
String temp1 = "暂无"; //临时属性变量
for(int j = 0;j < rows;j++){ //循环每一行
log.info("当前行数:{}",j);
//判断是否为空列,空行.解决读取出来的行数与实际有数据的行数不符,如果为空则自动跳过本页,防止空指针异常.并且记录下.
if(!StringUtils.hasText(sheet.getRow(j).getCell(0).toString()) &&
!StringUtils.hasText(sheet.getRow(j).getCell(1).toString()) &&
!StringUtils.hasText(sheet.getRow(j).getCell(2).toString()) ||
sheet.getRow(j).getCell(2).getCellType() == HSSFCell.CELL_TYPE_BLANK && //第3个属性为空
sheet.getRow(j).getCell(3).getCellType() == HSSFCell.CELL_TYPE_BLANK){ //第4个属性为空
//遇到无数据的行时,自动下一页.
log.debug("非正常退出,截止到"+j+"行,遇到空数据,自动下一页!");
erNum.add(k+1+"页-"+j+"行");
break;
}
shop = new Shop();
for(int i = 0; i < col; i++){ //循环每一列
System.out.println(sheet.getRow(j).getCell(i).toString());//getStringCellValue());
switch (i){
case 0: //设置属性
if(sheet.getRow(j).getCell(i).toString() != null &&
sheet.getRow(j).getCell(i).getCellType() != HSSFCell.CELL_TYPE_BLANK ){ //判断如果有属性的话就把存为临时变量
temp = sheet.getRow(j).getCell(i).toString();
shop.setProvince(sheet.getRow(j).getCell(i).toString()); //可替换代码(插入属性)
}else{ //否则没有的话就用之前的临时变量代替
shop.setProvince(temp);
}
break;
case 1: //设置建材属性
if(sheet.getRow(j).getCell(i).toString() != null &&
sheet.getRow(j).getCell(i).getCellType() != HSSFCell.CELL_TYPE_BLANK){
temp1 = sheet.getRow(j).getCell(i).toString();
shop.setBazaar(sheet.getRow(j).getCell(i).toString()); //可替换代码(插入属性)
}else{
shop.setBazaar(temp1); //可替换代码(插入属性)
}
break;
case 2: //设置属性
shop.setStore(sheet.getRow(j).getCell(i).toString());//可替换代码(插入属性)
break;
case 3: //设置属性
shop.setAddress(sheet.getRow(j).getCell(i).toString());//可替换代码(插入属性)
break;
case 4: //设置属性
shop.setPhone(sheet.getRow(j).getCell(i).toString()); //可替换代码(插入属性)
break;
case 5: //设置属性
shop.setResult(sheet.getRow(j).getCell(i).toString());//可替换代码(插入属性)
break;
}
}
list.add(shop); //添加一个对象,
System.out.println("-------------------------------------------------");
num ++;
if(( j + 1) == rows){
System.out.println("处理完本页数据:截止" + j + "行,即将处理下一页数据!");
num1++;
}
}
}
endTime = System.currentTimeMillis(); //计时结束
}catch (Exception e){
log.error("错误 ExcelHandle => readExcel() : {}",e.toString());
msg = "错误 ExcelHandle => readExcel() : " + e.toString();
}finally{
System.out.println("=========================");
System.out.println(" 读取结果");
System.out.println("=========================");
System.out.println("实际处理数据条数为:" + num);
System.out.println("完整无跳转处理页数:"+ num1);
System.out.println("总共处理页数:"+num2);
System.out.println("-------------------------");
System.out.println("自动触发跳转次数:" + erNum.size());
erNum.stream().forEach(t -> System.out.println(t));
System.out.println("-------------------------");
System.out.println("扫描时间:" + (endTime - startTime) + "ms"); //输出程序运行时间
System.out.println("错误信息:" + msg);
//封装数据并返回
analyse = new Analyse();
analyse.setNum(num);
analyse.setNum1(num1);
analyse.setNum2(num2);
analyse.setErNum(erNum);
analyse.setMsg(msg);
analyse.setList(list);
}
return analyse;
}
控制台输出结果信息:
提取出后即可以对数据进行持久化操作.如果需要其余代码的,可以在评论区留言.