Java使用Apache poi 导入Excel文件

2/10/2017来源:ASP.NET技巧人气:1235

java使用Apache poi 操作Excel-基本概念与使用中介绍了poi与excel相关的基本概念,接下来通过poi来导入excel。

本文将使用如下版本的 POI来完成导入数据的演示。

<dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>3.14</version> </dependency>

1 读取Excel并打印结果

由上篇文章可知Excel的基本结构,因此导入Excel就是要将Excel某个电子表单中的所有行读入到程序中。

public static <T> List<T> importExcel(File excelFile) throws InvalidFormatException, IOException, InstantiationException, IllegalaccessException { XSSFWorkbook workBook = new XSSFWorkbook(excelFile); XSSFSheet sheet = workBook.getSheetAt(0); for(int ri = sheet.getFirstRowNum() + 1 ; ri <= sheet.getLastRowNum(); ri++) { Row row = sheet.getRow(ri); for(int ci = row.getFirstCellNum(); ci < row.getLastCellNum(); ci++) { Cell cell = row.getCell(ci); Object value = getCellValue(cell,fields[ci]); System.out.PRintf("%s ",value); } System.out.println(""); } return result; } public static Object getCellValueByType(Cell cell) { int type = cell.getCellType(); switch (type) { case Cell.CELL_TYPE_BLANK: return null; case Cell.CELL_TYPE_BOOLEAN: return cell.getBooleanCellValue(); case Cell.CELL_TYPE_ERROR: return cell.getErrorCellValue(); case Cell.CELL_TYPE_FORMULA: return cell.getNumericCellValue(); case Cell.CELL_TYPE_NUMERIC: return cell.getNumericCellValue(); case Cell.CELL_TYPE_STRING: return cell.getStringCellValue(); default: }

如上代码是将excelFile读入,然后读取workBook.getSheetAt(0);表单,接着遍历各个行,并获取各个行的数据并输出。

由于Excel单元格的数据由不同的类型组成,因此获取数据时要判断单元格类型,并调对应的方法获取数据。

2 将读取的数据存入Java对象中

接下来将1中读取的数据保存在Java对象中。这里以Person对象为例。Person对象省略了Getter,Setter,及toString方法。

public class Person { private String name; private String sex; private Integer age; private Double height; }

使用的Excel数据如下所示。首行是标题列,首行之后是数据列。

姓名 性别 年龄 身高(M) 洛克 男 18 1.83 罗琳 女 18 1.76

为了将Excel中读取的数据存储在Java对象中,将1中的方法签名修改为,其中type是最终的类型,本文中为Person。

public static <T> List<T> importExcel(File excelFile,Class<T> type)

修改后的importExcel方法如下。

public static <T> List<T> importExcel(File excelFile,Class<T> type) throws InvalidFormatException, IOException, InstantiationException, IllegalAccessException { XSSFWorkbook workBook = new XSSFWorkbook(excelFile); List<T> result = new ArrayList<T>(); XSSFSheet sheet = workBook.getSheetAt(0); for(int ri = sheet.getFirstRowNum() + 1 ; ri <= sheet.getLastRowNum(); ri++) { Row row = sheet.getRow(ri); T object = type.newInstance(); Field[] fields = object.getClass().getDeclaredFields(); Field.setAccessible(fields, true); for(int ci = row.getFirstCellNum(); ci < row.getLastCellNum(); ci++) { Cell cell = row.getCell(ci); Object value = getCellValue(cell,fields[ci]); fields[ci].set(object, value); } result.add(object); Field.setAccessible(fields, false); } return result; }

上述代码忽略Excel标题行,因此是从首行+1行开始进行遍历。

代码中首先根据type来创建一个实例。 T object = type.newInstance(); 注意事项:这里需要注意的是对象的各个成员的类型要与Excel各列所保持一致,这样免去寻找对象成员与Excel列单元的对应关系的麻烦,并简化示例。

创建完实例后,获取对象中的各个字段,并修改访问权限,以便随后赋值。

Field[] fields = object.getClass().getDeclaredFields(); Field.setAccessible(fields, true);

接着遍历各个列,由于对象中字段的顺序与Excel列顺序保持一致,因此使用同样的列索引即可获取Excel列单元与对象成员。

Person成员的类型由String,int,double,而在Excel中单元格数值的类型统一被描述为Cell.CELL_TYPE_NUMERIC类型,因此在导入并转换为Java对象就需要格外注意类型处理,以避免出错。以本文中Excel中年龄为例,获取后单元格数值使用的是cell.getNumericCellValue()方法,该方法返回类型为Double,而Java对象中使用的是Integer。此外使用中也可能将年龄写成字符串。因此需要对每种数据类型判断并合理转换,或给出相应错误提示。

通过定义接口TypeHandler 来处理各种类型转换的问题。

public interface TypeHandler { public Object handle(Cell cell,Field field); }

以Integer类型为例,接着定义Integer处理器。

public class IntegerHandler implements TypeHandler{ @Override public Object handle(Cell cell, Field field) { int type = cell.getCellType(); if(type == Cell.CELL_TYPE_NUMERIC) { return (int)cell.getNumericCellValue(); } else if(type == Cell.CELL_TYPE_STRING) { try { return Integer.valueOf(cell.getStringCellValue()); }catch(NumberFormatException e) { return null; } } else { return null; } } }

当单元格类型返回double(Cell.CELL_TYPE_NUMERIC),则执行类型转换。如果是字符串类型,则尝试转换为整型。如果无法转换则返回null。对于无法转换的类型直接返回null。

其它类型如String,Double以处理方法与此一致。

最后修改getCellValue,代码如下。

private final static Map<Class<?>,TypeHandler> typeHandlerMap = new HashMap<>(); static { typeHandlerMap.put(Integer.class, new IntegerHandler()); typeHandlerMap.put(Double.class, new DoubleHandler()); typeHandlerMap.put(String.class, new StringHandler()); } private static Object getCellValue(Cell cell,Field field) { TypeHandler handler = typeHandlerMap.get(field.getType()); if(handler == null) { return null; } return handler.handle(cell, field); }

3 使用注解添加辅助功能

1,2中讲述了基本的导入功能实现。而对字段排序,验证等还可以结合自定义注解来完成,也就是在获取对象Field时,拿到注解并获取相关参数,最后完成校验排序等功能。

4 完整代码

private final static Map<Class<?>,TypeHandler> typeHandlerMap = new HashMap<>(); static { typeHandlerMap.put(Integer.class, new IntegerHandler()); typeHandlerMap.put(Double.class, new DoubleHandler()); typeHandlerMap.put(String.class, new StringHandler()); } public static <T> List<T> importExcel(File excelFile,Class<T> type) throws InvalidFormatException, IOException, InstantiationException, IllegalAccessException { XSSFWorkbook workBook = new XSSFWorkbook(excelFile); List<T> result = new ArrayList<T>(); XSSFSheet sheet = workBook.getSheetAt(0); for(int ri = sheet.getFirstRowNum() + 1 ; ri <= sheet.getLastRowNum(); ri++) { Row row = sheet.getRow(ri); T object = type.newInstance(); Field[] fields = object.getClass().getDeclaredFields(); Field.setAccessible(fields, true); for(int ci = row.getFirstCellNum(); ci < row.getLastCellNum(); ci++) { Cell cell = row.getCell(ci); Object value = getCellValue(cell,fields[ci]); fields[ci].set(object, value); } result.add(object); Field.setAccessible(fields, false); } return result; } private static Object getCellValue(Cell cell,Field field) { TypeHandler handler = typeHandlerMap.get(field.getType()); if(handler == null) { return null; } return handler.handle(cell, field); } public interface TypeHandler { public Object handle(Cell cell,Field field); } public class IntegerHandler implements TypeHandler{ @Override public Object handle(Cell cell, Field field) { int type = cell.getCellType(); if(type == Cell.CELL_TYPE_NUMERIC) { return (int)cell.getNumericCellValue(); } else if(type == Cell.CELL_TYPE_STRING) { try { return Integer.valueOf(cell.getStringCellValue()); }catch(NumberFormatException e) { return null; } } else { return null; } } } public class DoubleHandler implements TypeHandler{ @Override public Object handle(Cell cell, Field field) { int type = cell.getCellType(); if(type == Cell.CELL_TYPE_NUMERIC) { return cell.getNumericCellValue(); } else if(type == Cell.CELL_TYPE_STRING) { try { return Double.valueOf(cell.getNumericCellValue()); }catch(NumberFormatException e) { return null; } } else { return null; } } } public class StringHandler implements TypeHandler{ @Override public Object handle(Cell cell, Field field) { int type = cell.getCellType(); if(type != Cell.CELL_TYPE_STRING) { return ""; } else { return cell.getStringCellValue(); } } }

使用时要根据实际情况调整或新增handler即可。