先贴代码,然后解释几个容易出错的地方(工具类来自网上,我只负责解说)
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等类型,会报 无法赋值的错误。