使用Apache POI包導入Excel時是需要根據行和列取到對應的值,因此取值時需要知道該列所對應的值應存放到對象的那個字段中去,表格出現變動就會變的比較麻煩,因此此處使用自定義注解的方式,在對象中標明該屬性所對應的表頭,從程序中遍歷表頭找到與之對應的單元格,方便數據的導入。
所需的jar包:(用了一下工具類,因此多導入了兩個包)
<dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>3.17</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>3.17</version> </dependency> <dependency> <groupId>commons-beanutils</groupId> <artifactId>commons-beanutils</artifactId> <version>1.9.2</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.2.1</version> </dependency>
自定義注解:
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD ,ElementType.TYPE}) public @interface ExcelIn { /** * 導入sheet名稱 * @return */ String sheetName() default ""; /** * 字段對應的表頭名稱 * @return */ String title() default ""; }
接收導入數據的實體:
@ExcelIn(sheetName = "用戶信息") public class UserInfo { private String id; @ExcelIn(title = "姓名") private String name; @ExcelIn(title = "年齡") private Integer age; @ExcelIn(title = "出生日期") private Date birthday; }
導入Excel數據:
public class ExcelReader<T> { private static BeanUtilsBean beanUtilsBean = new BeanUtilsBean(); static { beanUtilsBean.getConvertUtils().register(new org.apache.commons.beanutils.converters.DateConverter(null), java.util.Date.class); } /** * 表頭名字和對應所在第幾列的下標,用於根據title取到對應的值 */ private final Map<String,Integer> title_to_index = new HashMap<>(); /** * 所有帶有ExcelIn注解的字段 */ private final List<Field> fields = new ArrayList<>(); /** * 統計表格的行和列數量用來遍歷表格 */ private int firstCellNum = 0; private int lastCellNum = 0; private int firstRowNum = 0; private int lastRowNum = 0; private String sheetName ; private HSSFSheet sheet ; public List<T> read(InputStream in , Class clazz) throws Exception { gatherAnnotationFields(clazz); configSheet(in); configHeader(); List rList= null; try { rList = readContent(clazz); } catch (IllegalAccessException e) { throw new Exception(e); } catch (InstantiationException e) { throw new Exception(e); } catch (InvocationTargetException e) { throw new Exception(e); } return rList ; } private List readContent(Class clazz) throws IllegalAccessException, InstantiationException, InvocationTargetException { Object o = null ; HSSFRow row = null ; List<Object> rsList = new ArrayList<>(); Object value = null ; for(int i = (firstRowNum+1);i<=lastRowNum;i++){ o = clazz.newInstance(); row = sheet.getRow(i); HSSFCell cell = null ; for (Field field : fields) { //根據注解中的title,取到表格中該列所對應的的值 Integer column=title_to_index.get(field.getAnnotation(ExcelIn.class).title()); if(column==null){ continue; } cell = row.getCell(column); value = getCellValue(cell) ; if(null != value && StringUtils.isNotBlank(value.toString())) { beanUtilsBean.setProperty(o, field.getName(), value); } } rsList.add(o); } return rsList ; } private void configSheet(InputStream in) throws Exception { //HSSFWorkbook:只能創建97-03版本的Excel,即:以xls結尾的Excel // 想要導入xlsx結尾的Excel,用XSSFWorkbook try(HSSFWorkbook wb = new HSSFWorkbook(in)){ getSheetByName(wb); } catch (FileNotFoundException e) { throw new Exception(e); } catch (IOException e) { throw new Exception(e); } } /** * 根據sheet獲取對應的行列值,和表頭對應的列值映射 */ private void configHeader(){ this.firstRowNum = sheet.getFirstRowNum() ; this.lastRowNum = sheet.getLastRowNum() ; //第一行為表頭,拿到表頭對應的列值 HSSFRow row = sheet.getRow(firstRowNum); this.firstCellNum = row.getFirstCellNum(); this.lastCellNum = row.getLastCellNum(); for (int i = firstCellNum;i<lastCellNum;i++){ title_to_index.put(row.getCell(i).getStringCellValue(),i); } } /** * 根據sheet名稱獲取sheet * @param workbook * @return * @throws Exception */ private void getSheetByName(HSSFWorkbook workbook) throws Exception { int sheetNumber = workbook.getNumberOfSheets(); for (int i = 0; i < sheetNumber; i++) { String name = workbook.getSheetName(i); if(StringUtils.equals(this.sheetName,name)) { this.sheet = workbook.getSheetAt(i); return; } } throw new Exception("excel中未找到名稱為"+this.sheetName+"的sheet"); } /** * 根據自定義注解,獲取所要導入表格的sheet名稱和需要導入的字段名稱 * @param clazz * @throws Exception */ private void gatherAnnotationFields(Class clazz) throws Exception { if(!clazz.isAnnotationPresent(ExcelIn.class)){ throw new Exception(clazz.getName()+"類上沒有ExcelIn注解"); } ExcelIn excelIn = (ExcelIn)clazz.getAnnotation(ExcelIn.class) ; this.sheetName = excelIn.sheetName(); // 得到所有定義字段 Field[] allFields = FieldUtils.getAllFields(clazz) ; // 得到所有field並存放到一個list中 for (Field field : allFields) { if (field.isAnnotationPresent(ExcelIn.class)) { fields.add(field); } } if( fields.isEmpty()){ throw new Exception(clazz.getName()+"中沒有ExcelIn注解字段"); } } private Object getCellValue(Cell cell) { if (cell == null) { return ""; } Object obj = null; switch (cell.getCellTypeEnum()) { case BOOLEAN: obj = cell.getBooleanCellValue(); break; case ERROR: obj = cell.getErrorCellValue(); break; case FORMULA: try { obj = String.valueOf(cell.getStringCellValue()); } catch (IllegalStateException e) { obj = numericToBigDecimal(cell); } break; case NUMERIC: obj = getNumericValue(cell); break; case STRING: String value = String.valueOf(cell.getStringCellValue()); value = value.replace(" ", ""); value = value.replace("\n", ""); value = value.replace("\t", ""); obj = value; break; default: break; } return obj; } private Object getNumericValue(Cell cell){ // 處理日期格式、時間格式 if (HSSFDateUtil.isCellDateFormatted(cell)) { return cell.getDateCellValue(); }else if (cell.getCellStyle().getDataFormat() == 58) { // 處理自定義日期格式:m月d日(通過判斷單元格的格式id解決,id的值是58) double value = cell.getNumericCellValue(); return org.apache.poi.ss.usermodel.DateUtil.getJavaDate(value); } else { return numericToBigDecimal(cell); } } private Object numericToBigDecimal(Cell cell) { String valueOf = String.valueOf(cell.getNumericCellValue()); BigDecimal bd = new BigDecimal(valueOf); return bd; } }
測試導入結果:
@RunWith(SpringRunner.class) @SpringBootTest public class Test { @org.junit.Test public void t(){ try{ File file = new File("d:/abc.xls"); ExcelReader<UserInfo> reader = new ExcelReader<>(); InputStream is = new FileInputStream(file); List<UserInfo> list = reader.read(is,UserInfo.class); if (CollectionUtils.isNotEmpty(list)) { for (UserInfo u : list) { System.out.println("姓名:" + u.getName() + " ,年齡:" + u.getAge() + " ,出身日期:" + u.getBirthday()); } } }catch (Exception e){ e.printStackTrace(); } } }
導入的Excel數據:(Excel的sheet名稱為接收實體對象的sheetName)
結果展示: