在使用poi实现excel批量导入和导出时遇到了很多问题,在实现用户信息批量导出成excel时,我将所有数据都使用excel的字符类型,这样就不用考虑excel类型与java类型的转换问题,但是实现导入的时候会有许多问题,将excel值传到java时就会有类型转换问题,因为你不能保证他全部按照字符类型来存储,而且excel能够自动根据输入值确定相应的类型,所以需要先判断类型再来选择java相应的类型来存储,excel类型的返回值是六个常量(0-5),我在使用时只有字符类型和数字类型是我使用的,但是在使用数字类型时会遇到类型转换异常的问题,后来发现是因为它们将日期类型也当做了数字类型且它们日期类型的格式为yyyy/MM/dd。先贴一波excel类型与java类型转换取值的代码:
/**
* 根据cell的类型获取相应的值
* @param cell
* @return
*/
public String typeConversion(Cell cell) {
if(cellJudge(cell)==0) {
return "";
}
if(cell.getCellType() == cell.CELL_TYPE_BOOLEAN) {
return String.valueOf(cell.getBooleanCellValue());
}else if(cell.getCellType()==cell.CELL_TYPE_NUMERIC) {
String cellvalue="";
if(HSSFDateUtil.isCellDateFormatted(cell)) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd");
Date dt = HSSFDateUtil.getJavaDate(cell.getNumericCellValue());
cellvalue = dateFormat.format(dt);
}else {
DecimalFormat df = new DecimalFormat("0");
cellvalue = df.format(cell.getNumericCellValue());
}
return cellvalue;
}else {
return String.valueOf(cell.getStringCellValue());
}
}
再说第二个问题,获取行数会出现一些问题,比如他原本有6行数据,删掉两条但是只删掉了两行的数据没有将两行彻底删除这个时候,去行数时会默认有6行而不是四行,在取值时就会出现一些空指针异常的问题,还有假如中间删除一行数据也会有这样的问题,所以这个地方要获取它实际的真实行数,但是api里好像并没有这样的方法,这里我使用的是下面的方法(由于本人只有一年经验写的代码可能有些糙,有需要的可以借鉴一下经验就好):
/**
* 获取实际的行数
* @param sheet
* @return
*/
public int getPracticalRows(Sheet sheet) {
int begin = sheet.getFirstRowNum();
int end = sheet.getLastRowNum();
int totalRows = 0;
for(int i=begin;i<=end;i++) {
if(null==sheet.getRow(i)) {
continue;
}
totalRows++;
}
return totalRows;
}
再说下一个问题,那就是某些单元格行数据为空时,有些excel行数据并没有填满,如果直接进行类型转换获取值时会出现空指针异常的问题,这里在类型转换前先判断单元格存不存在
/**
* 判断单元格是否为空
* @param cell
* @return
*/
public int cellJudge(Cell cell) {
if(null == cell||cell.getCellType()==HSSFCell.CELL_TYPE_BLANK||cell.equals("")) {
return 0;
}
return 1;
}
再说下一个问题,excel文件第一行数据实际一般并不是我们需要的东西,第一行一般是列名,所以我们获取数据实际要从第二行数据开始,我的需求是批量导入学生老师账号,所以我的主代码如下:
/**
* 将excel文件中的数据解析成Map<sheetName,List<Member>>的形式,
* 此方法用于将批量导入用户信息
* @param file 上传文件
* @return map 按班级批量导入学生
* @throws ParseException
*/
public Map<String,List<Member>> importExcel(MultipartFile file) throws ParseException{
Map<String,List<Member>> map = new HashMap<String,List<Member>>();
try {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
List<Member> list = new ArrayList<Member>();
//创建文件读取字节流
InputStream is = file.getInputStream();
//获取文件名加后缀
String fileName = file.getOriginalFilename();
//获取文件后缀
String suffix = fileName.substring(fileName.lastIndexOf(".")+1);
if(!"xls".equals(suffix) && !"xlsx".equals(suffix))
throw new ServiceException("文件格式不对");
Workbook workbook = new HSSFWorkbook(is);
//获取总表数
int sheets = workbook.getNumberOfSheets();
LOGGER.info("总表数:"+sheets);
//遍历表
for(int i=0;i<sheets;i++){
Sheet sheet = workbook.getSheetAt(i);
String sheetName = sheet.getSheetName();
//获取总行数
int rows = getPracticalRows(sheet);
LOGGER.info("总行数:"+rows);
if(rows <= 0)
continue;
//遍历行
for(int j=0;j<rows;j++){
//获取指定行
Row row = sheet.getRow(++j);
Member member = new Member();
member.setId(UUID.randomUUID().toString());
member.setName(typeConversion(row.getCell(0)));
member.setSex(typeConversion(row.getCell(1)));
member.setBirthDate(new java.sql.Date(sdf.parse(typeConversion(row.getCell(2))).getTime()));
member.setMobilePhoneNum(typeConversion(row.getCell(3)));
member.setQqNum(typeConversion(row.getCell(4)));
member.setEmail(typeConversion(row.getCell(5)));
member.setFamilyAddress(typeConversion(row.getCell(6)));
member.setFamilyPhoneNum(typeConversion(row.getCell(7)));
member.setUserName(typeConversion(row.getCell(8)));
member.setPassword(typeConversion(row.getCell(9)));
member.setCreateDate(new java.sql.Date(new Date().getTime()));
LOGGER.info(member.toString());
list.add(member);
}
map.put(sheetName, list);
}
} catch (IOException e) {
e.printStackTrace();
}
return map;
}
使用map的原因是在导入时可能是好几个班级进行导入,所以通过sheet值来将excel数据按班级区分再进行导入
至于得到map以后怎么处理,我相信这个大家都应该没什么问题,我也不贴代码了
最后在说一句,由于当时我没有将用户账号可能会出现导入相同的情况考虑进去,当用户账号一样时登录时可能出现一些问题,这个得根据公司实际的登录处理进行解决吧,当然我的方法并没有写的很完整,比如批量导入时不能在中间出现空行,第一行数据行不能为空。