Java使用Apache poi 导入Excel文件

本文介绍如何使用Java Apache POI库从Excel文件中导入数据,并将其存储为Java对象。文章详细展示了读取Excel文件的过程,包括处理不同类型的单元格数据,并通过自定义处理器进行类型转换。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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即可。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值