hutool poi、apache poi实现导入导出以及解析excel

一、前言

看了例子之后后续需要更加深入学习或者更多理解其他API的话,建议看官方文档。hutool项目是中国人维护的,有中文文档,阅读起来很方便。apache poi比较底层一点,可以更加自由去二次开发自己所需的功能。

hutool官方文档
hutool官方gitee
apache poi官方文档

二、基于hutool poi 实现导出excel文件到本地

有两种写法,可以一开始就为ExcelWriter 指定要写的文件,另一种是等到要flush的时候再指定对应文件的输出流。两种写法都能实现,不过第二种写法会显得更灵活一点,可以到后面再慢慢决定要输出到哪个文件。

具体实现逻辑
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class PoiTest {
    private int age;
    private String name;

   
    public PoiTest() {
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public PoiTest(int age, String name) {
        this.age = age;
        this.name = name;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        PoiTest poiTest = (PoiTest) o;
        return age == poiTest.age && Objects.equals(name, poiTest.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(age, name);
    }

    @Override
    public String toString() {
        return "PoiTest{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }

    public static void main(String[] args) throws IOException {
    	//方式一
        File file=new File("E:\\自测资料\\test.xlsx");
        file.createNewFile();
        ExcelWriter bigWriter = ExcelUtil.getBigWriter();
        bigWriter.setDestFile(file);
        List<PoiTest> list=new ArrayList<>();
        for (int i=0;i<10;i++){
            list.add(new PoiTest(18+i,"小明"+i));
        }
        //将数据写入到ExcelWriter的workbook中
        bigWriter.write(list);
        //将数据写入excel表
        bigWriter.flush();
        //关闭资源
        bigWriter.close();
		
//		//方式二
//        ExcelWriter bigWriter = ExcelUtil.getBigWriter();
//        List<PoiTest> list=new ArrayList<>();
//        for (int i=0;i<10;i++){
//            list.add(new PoiTest(18+i,"小明"+i));
//        }
//        bigWriter.write(list);
//
//        File file=new File("E:\\自测资料\\test.xlsx");
//        file.createNewFile();
//        OutputStream outputStream=new FileOutputStream(file);
//        bigWriter.flush(outputStream);
//        bigWriter.close();
    }
}

运行结果

在这里插入图片描述

三、基于hutool poi 实现从本地导入excel文件并转成对象列表

导入可以用map,list,对象来存储一行记录,推荐使用对象,方便后期使用。

具体实现逻辑
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class PoiTest {
    private int age;
    private String name;

   
    public PoiTest() {
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public PoiTest(int age, String name) {
        this.age = age;
        this.name = name;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        PoiTest poiTest = (PoiTest) o;
        return age == poiTest.age && Objects.equals(name, poiTest.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(age, name);
    }

    @Override
    public String toString() {
        return "PoiTest{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }

    public static void main(String[] args) throws IOException {
//方式一
        File file=new File("E:\\自测资料\\test.xlsx");
        ExcelReader reader = ExcelUtil.getReader(file);
        //list中的每个map对应excel表中的一行记录,excel表第一行(字段名)当作key,value就是行记录对应列的值
        List<Map<String, Object>> maps = reader.readAll();
        for(Map map:maps){
            Set<String> set = map.keySet();
            for(String key:set){
                System.out.println(key+"-->"+map.get(key));
            }
            System.out.println("-----------");
        }
        reader.close();
	
//方式二	
//        //list返回,内部的list,每一个list代表一行记录,第一行记录可以当作表头。
//        List<List<Object>> read = reader.read();
//        for(List list:read){
//            for (Object o:list){
//                System.out.println(o);
//            }
//            System.out.println("-----");
//        }
//        reader.close();

//方式三
//        //根据表头名和实体对象字段名来匹配记录,并返回实体列表
//        File file=new File("E:\\自测资料\\test.xlsx");
//        ExcelReader reader = ExcelUtil.getReader(file);
//        /**
//         *  第一个参数是表头所在行;第二个参数是读取数据开始行,会自动过滤掉表头行;第三个参数是实体类
//         *  如果行记录所有列都为空,则会跳过该行记录,不创建对象。只要有一个列是不为空的,就会创建对象
//         **/
//        List<PoiTest> read = reader.read(0, 0, PoiTest.class);
//        reader.close();
//        for(PoiTest poiTest:read){
//            System.out.println(poiTest);
//        }
    }
}

运行结果

map方式返回结果

在这里插入图片描述
list方式返回结果:(excel中只要行记录存在,那么空的列会保存为空字符串)
在这里插入图片描述
对象列表方式返回结果:
在这里插入图片描述

四、基于apache poi实现根据传入的类将excel的输入流解析成类对象(excel解析原理)

这个就是我们日常使用的excel上传功能,因此非常有必要掌握原理。
一开始就想过可以通过传入类名,结合反射机制来创建对象,还可以获取对象的字段并赋值,实际上思路也确实大概这样。我从chat-gpt拿到了一个解析excel输入流的demo,不过这个demo并没有把excel表第一行当作表头;因此,我在这个基础上做了一些修改,实现了解析excel文件输入流成对象的工具类方法。(tips:支持的数据类型只能是比较基础的类型,不能是复杂的对象,复杂对象先json字符串接收,后面再自己转)
动态代理太酷啦!!!

当然,根据hutool的工具类就有现成的方法。
ExcelReader reader=getReader(InputStream bookStream)
调用reader的各种方法就可以实现解析成各种形式的对象了。

实体类对象(也就是要解析成的对象)
package com.yumoxuan.pojo;
import java.util.Date;
import java.util.Objects;
public class Person {
    private int age;
    private String name;
    private Date birthday;
    private double high;

    public Person() {
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public double getHigh() {
        return high;
    }

    public void setHigh(double high) {
        this.high = high;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age &&
                Double.compare(person.high, high) == 0 &&
                Objects.equals(name, person.name) &&
                Objects.equals(birthday, person.birthday);
    }

    @Override
    public int hashCode() {
        return Objects.hash(age, name, birthday, high);
    }

    @Override
    public String toString() {
        return "Person{" +
                "age=" + age +
                ", name='" + name + '\'' +
                ", birthday=" + birthday +
                ", high=" + high +
                '}';
    }
}
具体实现逻辑
import com.yumoxuan.pojo.Person;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.lang.reflect.Field;
public class ExcelParser {
    public static <T> List<T> parseExcelFile(InputStream inputStream, Class<T> clazz) {
        List<T> objectList = new ArrayList<>();

        try (Workbook workbook = new XSSFWorkbook(inputStream)) {
            Sheet sheet = workbook.getSheetAt(0); // Assuming the data is in the first sheet

            // Get the field names from the class
            Field[] fields = clazz.getDeclaredFields();
             List<String> objectFieldNameList= Arrays.stream(fields).map(o->o.getName()).collect(Collectors.toList());
            //获取excel第一行作为对象的字段名
                       List<String> excelFieldNameList = new ArrayList<>();
            boolean firstRow = true;
            // Iterate over rows in the sheet
            for (Row row : sheet) {
                T object = clazz.newInstance(); // Create an instance of the class

                // Map cell values to object fields
                int i = 0;
                for (Cell cell : row) {
                    if (firstRow) {
                        //获取excel表的第一行作为字段名
                        excelFieldNameList.add(cell.getStringCellValue());
                        continue;
                    }
                   	
                    if(!objectFieldNameList.contains(excelFieldNameList.get(i))){
                        //过滤excel中存在,但类中不存在的字段
                        continue;
                    }
                    //注意,这里如果尝试获取类里面没有的字段,会抛异常,因此excel表结构最好协商定下来。当然有了上一步的判断,这个问题不会发生
                    Field field = clazz.getDeclaredField(excelFieldNameList.get(i));
                    //开启编辑权限
                    field.setAccessible(true);
                    Class<?> fieldType = field.getType();

                    // 这里只需要把可能的数据类型都加上就可以了;不过一般也就那么基本数据类型加上字符串
                    if (fieldType == String.class) {
                        field.set(object, cell.getStringCellValue());
                    } else if (fieldType == int.class || fieldType == Integer.class) {
                        field.set(object, (int) cell.getNumericCellValue());
                    } else if (fieldType == boolean.class || fieldType == Boolean.class) {
                        field.set(object, cell.getBooleanCellValue());
                    } else if (fieldType == double.class || fieldType == Double.class) {
                        field.set(object, cell.getNumericCellValue());
                    } else if (fieldType == float.class || fieldType == Float.class) {
                        field.set(object, (float) cell.getNumericCellValue());
                    } else if (fieldType == Date.class) {
                        field.set(object, cell.getDateCellValue());
                    } else {
                        //暂不支持的类型
                    }
                    i++;
                    //关闭编辑权限
                    field.setAccessible(false);
                }
                if (!firstRow) {
                    objectList.add(object);
                }
                firstRow = false;
            }
        } catch (IOException | ReflectiveOperationException e) {
            e.printStackTrace();
        }

        return objectList;
    }

    public static void main(String[] args) throws FileNotFoundException {
        File file = new File("F:\\JAVA\\test.xlsx");
        InputStream inputStream = new FileInputStream(file);
        List<Person> people = ExcelParser.parseExcelFile(inputStream, Person.class);
        for (Person person : people) {
            System.out.println(person);
        }
    }
}

excel文件路径及数据

在这里插入图片描述

运行结果

Person{age=18, name=‘张三’, birthday=Mon Dec 25 12:13:00 CST 2000, high=163.25}
Person{age=19, name=‘李四’, birthday=Tue Dec 26 12:13:00 CST 2000, high=180.25}

学无止境,共勉。如果有帮到你,给我点个赞吧。

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值