以下列出实现导入功能需要的各个方法,循序渐进
/**
* 创建Workbook工作薄对象
*
* @param file 上传的文件
* @return
*/
public static Workbook getWorkBook(MultipartFile file) {
//获得文件名
String fileName = file.getOriginalFilename();
//创建Workbook工作薄对象,表示整个excel
Workbook workbook = null;
try {
//获取excel文件的io流
InputStream is = file.getInputStream();
//根据文件后缀名不同(xls和xlsx)获得不同的Workbook实现类对象
if (fileName.endsWith("xls")) {
//2003,填充导入excel文件数据进入工作簿
workbook = new HSSFWorkbook(is);
} else if (fileName.endsWith("xlsx")) {
//2007 及2007以上,填充导入excel文件数据进入工作簿
workbook = new XSSFWorkbook(is);
}
} catch (IOException e) {
throw new BusinessException("上传文件生成工作簿出错");
}
return workbook;
}
/**
* excel表格中数据的转换
*
* @param cell
* @return
*/
public static String getCellValue(Cell cell) {
String cellValue = "";
if (Objects.isNull(cell)) {
return cellValue;
}
int cellType = cell.getCellType();
//根据数据的类型进行相应转换
switch (cellType) {
//数字类型
case Cell.CELL_TYPE_NUMERIC:
cellValue = String.valueOf(cell.getNumericCellValue());
break;
//字符串类型
case Cell.CELL_TYPE_STRING:
cellValue = String.valueOf(cell.getStringCellValue());
break;
//布尔类型
case Cell.CELL_TYPE_BOOLEAN:
cellValue = String.valueOf(cell.getBooleanCellValue());
break;
//公式类型
case Cell.CELL_TYPE_FORMULA:
cellValue = String.valueOf(cell.getCellFormula());
break;
//空值类型
case Cell.CELL_TYPE_BLANK:
cellValue = "";
break;
//错误类型
case Cell.CELL_TYPE_ERROR:
cellValue = "错误类型";
break;
default:
cellValue = "";
break;
}
return cellValue;
}
/**
* 对字段做类型转换(类型和实体一一对应,如果有其他类型,可以对该方法进行扩展)
*
* @param fieldValue 字段值
* @param fieldType 字段类型
* @param fieldName 字段名称
* @param line excel行数
* @return
*/
public static Object typeCast(String fieldValue, Class fieldType, String fieldName, String line) {
Object value = fieldValue;
if (Objects.nonNull(value)) {
//判断字段是否是日期类型
if (fieldType.isAssignableFrom(Date.class)) {
value = DateUtils.parseDate(fieldValue);
if (Objects.isNull(value)) {
//抛出自定义的业务异常,给前端显示,提示用户的第几行哪个字段出了错
throw new BusinessException("第"+line+"行"+fieldName+"格式有误");
}
}
//判断字段是否是数字类型
if (fieldType.isAssignableFrom(BigDecimal.class)) {
try {
value = new BigDecimal(fieldValue);
} catch (Exception e) {
//抛出自定义的业务异常,给前端显示,提示用户的第几行哪个字段出了错
throw new BusinessException("第"+line+"行"+fieldName+"格式有误");
}
}
}
return value;
}
/**
* 把map转换成实体
*
* @param mapList 数据的map集合
* @param pojoClass 数据实例类型
* @return
* @throws Exception
*/
public static List<Object> convertMapToEntity(List<Map<String, String>> mapList, Class<?> pojoClass) throws Exception {
//对象集合
List<Object> objectList = new ArrayList<>();
//集合初始化行数(方便之后错误提示可以具体到行数)
int line = 3;
//遍历mapList
for (Map<String, String> entityMap : mapList) {
//实例化
Object object = pojoClass.newInstance();
//反射取得类各个字段
Field[] fields = pojoClass.getDeclaredFields();
//遍历map
for (Map.Entry<String, String> entry : entityMap.entrySet()) {
String key = entry.getKey();
//遍历对象字段
for (Field field : fields) {
//取得注解属性和字段名称
Excel excel = field.getAnnotation(Excel.class);
Class fieldType = field.getType();
if (Objects.nonNull(excel)) {
String fieldName = excel.importName();
//如果注解中的importName(导入字段名)和key(excel标题)一致,则进行设值
if (StringUtils.isNotEmpty(fieldName) && key.equals(fieldName)) {
String fieldValue = entityMap.get(key);
field.setAccessible(true);
Object value = null;
if (!StringUtil.isBlank(fieldValue)) {
value = typeCast(fieldValue, fieldType, fieldName, String.valueOf(line));
}
field.set(object, value);
break;
}
}
}
}
objectList.add(object);
line++;
}
return objectList;
}
/**
* 将上传的exel文件转成对应实体集合
*
* @param file 上传的导入文件
* @param pojoClass 导出的数据的class
* @return
*/
public static List<Object> importExel(MultipartFile file, Class<?> pojoClass) throws Exception {
//创建excel工作簿
Workbook workbook = getWorkBook(file);
int index = 0;
//获取第一个sheet
Sheet sheet = workbook.getSheetAt(0);
//把每行转换成<标题名,值>的map集合
List<Map<String, String>> mapList = new ArrayList<>();
//获取标题行
Row titleRow = sheet.getRow(0);
//开始的单元格下标
int startCell = titleRow.getFirstCellNum();
//末尾的单元格下标
int endCell = titleRow.getLastCellNum();
//遍历excel行数
for (Row row : sheet) {
//取得该行行数
int rowNum = row.getRowNum();
//我这里有两行标题行,所以row要大于1,才能去取数据,而row.getCell(4) != null是为了判断该行数据是不是空的,excel定义个序号列,必填项,序号不为空才代表这行有数据,可以去读取
if (rowNum > 1 && row.getCell(0) != null) {
//map给个初始长度,因为大部分情况,作为开发,是知道到底要导入多少字段的
Map<String, String> valueMap = new HashMap<>(60);
for (int i = startCell; i < endCell; i++) {
//取得表头名
String title = titleRow.getCell(i).getStringCellValue();
//取得单元格数值
String value = Objects.nonNull(row.getCell(i)) ? getCellValue(row.getCell(i)) : null;
valueMap.put(title, value);
}
mapList.add(valueMap);
}
}
//转化完后集合要是为空,说明没数值,给出提示
if (CollectionUtils.isEmpty(mapList)) {
throw new BusinessException("暂无数据可导入");
}
//最终返回要导入的实例集合
return convertMapToEntity(mapList, pojoClass);
}
使用方法:
-
将注解定义在要导入的实体字段上,类似:
2.直接调用上面列出的importExel(MultipartFile file, Class<?> pojoClass) 方法,传入对应参数就行
3.将返回的Object集合转换成你要导入的实体集合,调用批量新增的接口
小建议:
这里可以创建一个导入结果类,类似:
然后将successImportList做插入数据库操作
errorImportList再加一个导入错误信息的字段,将导入的错误写到这个字段,然后调用导出方法,将errorImportList数据导出,方便用户查看原因。
转载:https://blog.csdn.net/weixin_38106322/article/details/101777220
查看评论