先贴代码,然后解释几个容易出错的地方(工具类来自网上,我只负责解说)
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.myproject.common.normalUtils.excel.constant.ExcelType;
import com.myproject.common.normalUtils.excel.util.BeanUtils;
/**
* excel解析
*
*/
public class ExcelExtractor {
private static Logger logger = LoggerFactory.getLogger(ExcelExtractor.class);
/**
* 解析excel,默认第一行为表头
*
* @param type excel类型
* @param excel excel输入流
* @param tClass 要生成的实体类
* @param headerMapper 实体类和表格第一行的映射,key是表格,val是实体类name
* @param sheetIndex 需要解析的工作表,第一张是 0
* @return 解析后集合
* @throws IllegalArgumentException 解析错误
*/
public static <T> List<T> extractFrom(ExcelType type, InputStream excel, Class<T> tClass,
Map<String, String> headerMapper, Integer sheetIndex) throws IllegalArgumentException, IOException {
Workbook workbook;
try {
workbook = type.equals(ExcelType.XLS) ? new HSSFWorkbook(excel) : new XSSFWorkbook(excel);
} catch (IOException e) {
throw new IllegalArgumentException("未知的表格类型");
}
//解析表头.默认第一行为表头
Sheet sheet = workbook.getSheetAt(sheetIndex);
logger.debug("总行数:{}",sheet.getLastRowNum());
Iterator<Row> rowIterator = sheet.rowIterator();
//第一行
Row firstRow = rowIterator.next();
Iterator<Cell> firstCell = firstRow.cellIterator();
Set<String> existHeaderSet = new HashSet<>();
List<String> existHeader = new ArrayList<>(headerMapper.size());
firstCell.forEachRemaining(cell -> {
if (cell.getCellType() != Cell.CELL_TYPE_STRING) {
throw new IllegalArgumentException("第一行必须全部为字符串,第" + cell.getColumnIndex() + 1 + "有问题");
}
existHeaderSet.add(cell.getStringCellValue());
existHeader.add(cell.getStringCellValue());
});
//查询出不存在的表头
for (String s : headerMapper.keySet()) {
if (!existHeaderSet.contains(s)) {
throw new IllegalArgumentException("不存在的表头:" + s);
}
}
//开始解析表单
Integer lineNumber = 0;
List<T> result = new ArrayList<T>();
try {
while (rowIterator.hasNext()){
Row x = rowIterator.next();
lineNumber++;
Map<String, Object> tempMap = new HashMap<>();
Iterator<Cell> cellIterator = x.cellIterator();
cellIterator.forEachRemaining(y -> {
Object value = null;
switch (y.getCellType()) {
case Cell.CELL_TYPE_STRING:
value = y.getStringCellValue();
break;
case Cell.CELL_TYPE_BOOLEAN:
value = y.getBooleanCellValue();
break;
case Cell.CELL_TYPE_NUMERIC:
value = y.getNumericCellValue();
break;
case Cell.CELL_TYPE_ERROR:
value = "";
break;
case Cell.CELL_TYPE_BLANK:
value = "";
break;
default:
value = "";
}
String key = headerMapper.get(existHeader.get(y.getColumnIndex()));
tempMap.put(key, value);
});
result.add(BeanUtils.map2Bean(tempMap, tClass));
}
} catch (Exception e) {
logger.error("解析错误",e);
logger.error("第{}行解析错误",lineNumber);
}
return result;
}
}
/**
* map转bean
* @param source map属性
* @param instance 要转换成的实体类
* @return
*/
public static <T> T map2Bean(Map<String, Object> source, Class<T> instance) {
try {
T object = instance.newInstance();
Field[] fields = object.getClass().getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
FieldName fieldName = field.getAnnotation(FieldName.class);
if (fieldName != null){
field.set(object,source.get(fieldName.value()));
}else {
field.set(object,source.get(field.getName()));
}
}
return object;
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
/**
* 定义excel格式
*/
public enum ExcelEnums {
XLS, XLSX;
}
备注:
1. 数值类型处理。通过POI取出的数值默认都是double,即使excel单元格中存的是1,取出来的值也是1.0,这就造成了一些问题,如果数据库字段是int,那么就会wrong data type,所以需要对数值类型处理。
Double doubleVal=y.getNumericCellValue();
if (!isEmpty(y) && y.getCellType() == Cell.CELL_TYPE_NUMERIC) {
long longVal = Math.round(y.getNumericCellValue());
if (Double.parseDouble(longVal + ".0") == doubleVal)
value = Integer.parseInt(String.valueOf(longVal));
else
value = doubleVal;
}
这么处理后,单元格中的小数没有变化,如果是整数,也会取到整数。
2.日期类型处理。POI对单元格日期处理很弱,没有针对的类型,日期类型取出来的也是一个double值,所以同样作为数值类型。
3.map2Bean()。
FieldName fieldName = field.getAnnotation(FieldName.class);
if (fieldName != null){
field.set(object,source.get(fieldName.value()));
}else {
field.set(object,source.get(field.getName()));
}
如果实体类字段是double等类型,会报 无法赋值的错误。