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

news/2024/7/21 6:09:51 标签: excel, apache, java

一、前言

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

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

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

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

具体实现逻辑

java">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();
    }
}

运行结果

在这里插入图片描述

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

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

具体实现逻辑

java">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_poiexcel_222">四、基于apache poi实现根据传入的类将excel的输入流解析成类对象(必学***)

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

实体类对象(也就是要解析成的对象)

java">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 +
                '}';
    }
}

具体实现逻辑

java">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_388">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}

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


http://www.niftyadmin.cn/n/438225.html

相关文章

高频题目总结

数组中的第 k 大的数字 class Solution:def findKthLargest(self, nums: List[int], k: int) -> int:n len(nums)def partitions(arr, l, r):base arr[l]while l < r:while l < r and arr[r] > base:r - 1arr[l] arr[r]while l < r and arr[l] < base:l …

JDK,JRE,JVM有什么区别?跨平台?跨语言?

JDK Java Development Kit&#xff08;Java开发工具包&#xff09;&#xff0c;提供了Java的开发环境和运行环境。包含Java源文件的编译器Javac&#xff0c;还有调试和分析工具。 JRE Java Runtime Environment&#xff08;Java运行环境&#xff09;包含了Java虚拟机&#xff…

想了解3,4,6-Tri-O-acetyl-D-galactal,4098-06-0,D-三乙酰半乳糖烯?点击这里查看详细信息!

文章关键词&#xff1a;糖化学试剂&#xff0c;三乙酰半乳糖烯 3,4,6-Tri-O-acetyl-D-galactal |3,4,6-O-三乙酰基-D-半乳糖烯&#xff0c;D-三乙酰半乳糖烯|CAS&#xff1a;4098-06-0 | 纯度&#xff1a;95%一、结构式&#xff1a; 二、试剂参数信息&#xff1a; CAS&#x…

vue3创建新项目报错:Cannot find module ‘xxx‘

创建vue3项目&#xff0c;命令&#xff1a; 1. 使用vite创建&#xff1a; npm create vitelatest my-vue-app -- --template vue-ts 2.使用vue官方命令&#xff1a; npm init vuelatest 创建vue3模板学习vue3.3的新语法&#xff0c;创建完成后发现文件很多爆红&#xff1a…

【编程语言 · C语言 · 递归函数】

递归函数 C 语言的函数都支持递归, 也就是说&#xff0c;每个函数都可以直接或者间接第调用自己。所谓的间接调用&#xff0c;是指在递归函数调用的下层函数中再调用自己。 递归关系图如下&#xff1a; 递归之所以能实现&#xff0c;是因为函数的每个执行过程在栈中都有自己的…

嵌套虚拟机-Win10下的-wmware中的Ubuntu1804-使用KVM-安装win和ubuntu虚拟机

一、物理机操作 参考博文-CSDN-林麦安 -关于“ VMware Workstation 16 此平台不支持虚拟化的Intel VT-x/EPT. 不使用虚拟化的Intel VT-x/EPT,是否继续&#xff1f;”的有关问题的总结解答 在windows物理机搜索&#xff1a;内核隔离 把开关置为关 但是我的物理机这个选项已经是…

[学习笔记] [机器学习] 12. [上] HMM 隐马尔可夫算法(马尔科夫链、HMM 三类问题、前后后向算法、维特比算法、鲍姆-韦尔奇算法、API 及实例)

学习目标&#xff1a; 了解什么是马尔科夫链知道什么是 HMM 模型知道前向后向算法评估观察序列概率知道维特比算法解码隐藏状态序列了解鲍姆-韦尔奇算法知道 HMM 模型 API 的使用 1. 马尔科夫链 学习目标&#xff1a; 知道什么是马尔科夫链 在机器学习算法中&#xff0c;马…

【Java入门】-- Java基础详解之 [数组、冒泡排序]

目录 1.为什么需要数组&#xff1f; 2.数组的介绍 3.数组的快速入门 4.数组的使用 5.动态初始化 6.静态初始化 7.数组的细节 8.数组的赋值机制 9.数组拷贝 10.数组反转 11.二维数组 12.冒泡排序 1.为什么需要数组&#xff1f; 有五个学生&#xff0c;他们英语成绩…