阿里巴巴-EasyExcel 基于Java的简单、省内存的读写Excel

简介: 该文章主要介绍了在Java应用中如何使用EasyExcel技术完成对Excel文件的导入和导出操作,包括环境搭建、基本概念、快速入门、进阶操作和综合应用等内容,并提供了相关代码示例和注意事项。

课程介绍

本博客主要讲解在java应用中如何利用EasyExcel技术完成对excel文件的导入和导出操作;

黑马阿里EasyExcel实战教程,阿里开源技术实现MySQL和Excel之间海量数据处理_哔哩哔哩_bilibili

技术要求

1. java基础及web基础

2. SSM(SpringMVC+Spring+Mybatis)

3. mysql数据库

基本概念

1. EasyExcel是什么

EasyExcel是一个基于Java的简单、省内存的读写Excel的阿里开源项目。

在尽可能节约内存的情况下支持读写百M的Excel。

2. EasyExcel 能用在哪里

项目中涉及到Excel文件,CSV文件大多数的读写操作,均可以使用!

3. 为什么要选用EasyExcel解析excel

4. 如何使用

阿里官方文档: EasyExcel官方文档 - 基于Java的Excel处理工具 | Easy Excel

快速入门

1:环境搭建:

1)创建maven工程

2)引入相关坐标

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>3.1.2</version>
</dependency>

3)参考官方API完成功能

2: 简单写excel

获取代码路径的工具类,可以获取到模块这一级的磁盘路径

package com.ithiema.utils;
/*
    获取代码路径的工具类,可以获取到模块这一级的磁盘路径;
 */
public class TestFileUtil {
    public static String getPath() {
        return TestFileUtil.class.getResource("/").getPath().replace("classes/","");
    }

    public static void main(String[] args) {
        System.out.println(getPath());
    }
}

1)编写模型类并加入注解

import com.alibaba.excel.annotation.ExcelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Employee {
    // 成员变量
    @ExcelProperty("员工工号")
    private int id;
    @ExcelProperty("员工姓名")
    private String name;
    @ExcelProperty("员工工资")
    private double salary;
    @ExcelProperty("入职日期")
    private Date date;
}

2)编写获取测试数据的方法

3)调用官方API完成写功能

不知道为什么 我测试的时候,路径一直识别不到,报错

com.alibaba.excel.exception.ExcelGenerateException: Can not found file.

    at com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder.<init>(WriteWorkbookHolder.java:167)
    at com.alibaba.excel.context.WriteContextImpl.initCurrentWorkbookHolder(WriteContextImpl.java:107)
    at com.alibaba.excel.context.WriteContextImpl.<init>(WriteContextImpl.java:90)
    at com.alibaba.excel.write.ExcelBuilderImpl.<init>(ExcelBuilderImpl.java:36)
    at com.alibaba.excel.ExcelWriter.<init>(ExcelWriter.java:36)
    at com.alibaba.excel.write.builder.ExcelWriterBuilder.build(ExcelWriterBuilder.java:114)
    at com.alibaba.excel.write.builder.ExcelWriterBuilder.sheet(ExcelWriterBuilder.java:130)
    at com.alibaba.excel.write.builder.ExcelWriterBuilder.sheet(ExcelWriterBuilder.java:126)
    at com.ithiema.write.SimpleWrite.write(SimpleWrite.java:30)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
    at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
    at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
    at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
    at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
    at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)

路径改成这种才行 String fileName = "D:\\MLdata\\1predict.xlsx";

/*
    练习easyexcel的简单写数据
 */
public class SimpleWrite {

    @Test
    public void testWrite(){
        String name = TestFileUtil.getPath() + "简单写数据" + System.currentTimeMillis() + ".xlsx";
        // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
        // 如果这里想使用03 则 传入excelType参数即可        
        EasyExcel.write(name, Employee.class).sheet("我的快速入门").doWrite(data(100000));
    }

    // 准备测试数据的方法
    private List<Employee> data(int i) {
        List<Employee> list = new ArrayList<>();
        for (int j = 1; j <= i; j++) {
            list.add(new Employee(j,"测试数据"+j,6.6*j,new Date()));
        }
        return list;
    }
}

核心代码:

EasyExcel.write(fileName, DemoData.class).sheet("测试").doWrite(data(100000));

3: 简单读excel

1)编写模型类并加入注解

还是上面的Employee模型

2)监听器介绍

PageReadListener 阿里自定义的监听器

3)调用官方API完成写功能

package com.ithiema.read;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.read.listener.PageReadListener;
import com.ithiema.pojo.Employee;
import com.ithiema.utils.TestFileUtil;
import org.junit.Test;

import java.io.File;

/*
    快速入门读数据
 */
public class SimpleReader {
    @Test
    public void read(){
        //JDK8+,自定义监听器 since:3.0.0-beta1
        String fileName = TestFileUtil.getPath() + "simpleWrite1669290881801.xlsx";
        // 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭
        // 这里每次会读取100条数据 然后返回过来 直接调用使用数据就行
        EasyExcel.read(fileName, Employee.class, new PageReadListener<Employee>(dataList -> {
            for (Employee demoData : dataList) {
                System.out.println(demoData);
            }
        })).sheet().doRead();
    }
}

核心代码:

EasyExcel.read(fileName, DemoData.class, new PageReadListener<DemoData>
(list -> System.out.println(list))).sheet().doRead();

进阶操作

1: 批量(重复)写数据

1)编写模型类并加入注解

2)调用官方API完成写功能

package com.ithiema.write;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.util.ListUtils;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.ithiema.pojo.Employee;
import com.ithiema.utils.TestFileUtil;
import org.junit.Test;

import java.util.Date;
import java.util.List;

/*
    批量写数据
 */
public class ManyWrite {
    // 准备测试数据的方法
    private List<Employee> data(int count) {
        List<Employee> list = ListUtils.newArrayList();
        for (int i = 1; i <= count; i++) {
            list.add(new Employee(i,"测试数据"+i,new Date(),6.6*i));
        }
        return list;
    }

    // 批量写数据  100万
    @Test
    public void write(){
        // 方法2: 如果写到不同的sheet 同一个对象
        String fileName = TestFileUtil.getPath() + "repeatedWrite" + System.currentTimeMillis() + ".xlsx";
        // 这里 指定文件
        try (ExcelWriter excelWriter = EasyExcel.write(fileName, Employee.class).build()) {
            // 去调用写入,这里我调用了五次,实际使用时根据数据库分页的总的页数来。这里最终会写到5个sheet里面
            WriteSheet writeSheet = EasyExcel.writerSheet("测试数据").build();
            long t1 = System.currentTimeMillis();
            for (int i = 0; i < 100; i++) {
                // 每次都要创建writeSheet 这里注意必须指定sheetNo 而且sheetName必须不一样
                // 分页去数据库查询数据 这里可以去数据库查询每一页的数据
                List<Employee> data = data(10000);
                excelWriter.write(data, writeSheet);
            }
            long t2 = System.currentTimeMillis();
            System.out.println(t2-t1);
        }
    }
}
try (ExcelWriter writer = EasyExcel.write(fileName, DemoData.class).build()) {
            WriteSheet writeSheet = EasyExcel.writerSheet("测试表1").build();
            for (int i = 0; i < 3; i++) {
                ExcelWriter write = writer.write(data(100000), writeSheet);
            }
        }

3) 上面写的数据格式不好看怎么办?

利用阿里提供的模版写数据,模版设置的样式新添加的数据会自动包含样式

2: 按模版填充单个对象数据

1)编写模型类并加入注解

2)按要求编写模版文件

3)调用官方API完成写功能

/* 练习填充数据
 */
public class FillWriter {
    // 准备测试数据的方法
    private List<Employee> data(int count) {
        List<Employee> list = ListUtils.newArrayList();
        for (int i = 1; i <= count; i++) {
            list.add(new Employee(i,"测试数据"+i,new Date(),6.6*i));
        }
        return list;
    }

    // 批量写数据  100万
    @Test
    public void write(){
        // 方案3 分多次 填充 会使用文件缓存(省内存)
        String fileName = TestFileUtil.getPath() + "listFill" + System.currentTimeMillis() + ".xlsx";
        String templateFileName = TestFileUtil.getPath() + "模版.xlsx";
        try (ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build()) {
            WriteSheet writeSheet = EasyExcel.writerSheet().build();
            long t1 = System.currentTimeMillis();
            for (int i = 0; i < 100; i++) {
                excelWriter.fill(data(10000), writeSheet);
            }
            long t2 = System.currentTimeMillis();
            System.out.println(t2-t1);
        }
    }
}

EasyExcel.write(fileName).withTemplate(templateFileName).sheet().doFill(fillData);

3: 按模版批量填充多个对象数据

1)编写模型类并加入注解

2)按要求编写模版文件

3)调用官方API完成写功能

try (ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build()) {
            WriteSheet writeSheet = EasyExcel.writerSheet(0).build();
            WriteSheet writeSheet2 = EasyExcel.writerSheet("数据2").build();
            excelWriter.fill(data(300000), writeSheet);
            excelWriter.fill(data(300000), writeSheet2);
        }

4: 自定义监听器读海量(百万级别)数据并监控内存消耗

1)编写模型类并加入注解

2)自定义监听器

/*    自定义监听器读数据
 */
public class EmployeeListener implements ReadListener<Employee> {

    private int count = 100;
    private ArrayList<Employee> list = new ArrayList<>(count);
    private EmployeeDao dao;

    public EmployeeListener(EmployeeDao dao) {
        this.dao = dao;
    }

    // 每读一行数据,都会调用这个方法
    @Override
    public void invoke(Employee employee, AnalysisContext analysisContext) {
        // 将读取到的一行数据添加到集合
        list.add(employee);
        // 判断是不是到达缓存量了
        if(list.size()>=100){
            // 模拟操作数据库
            dao.save(list);
            list= new ArrayList<>(count);
        }
    }

    // 读完整个excel之后,会调用这个方法
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        if(list.size()>0){
            // 操作数据库
            dao.save(list);
            list= new ArrayList<>(count);
        }
    }
}

3)调用官方API完成写功能

/*    自定义监听器,读海量数据
 */
public class ManyRead {
    @Test
    public void read(){
        String fileName = TestFileUtil.getPath()+"repeatedWrite1669291976389.xlsx";
        ExcelReader reader = EasyExcel.read(fileName, Employee.class, 
            new EmployeeListener(new EmployeeDao())).build();
        ReadSheet sheet = EasyExcel.readSheet().build();
        reader.read(sheet);
    }
}
EasyExcel.read(fileName, IndexOrNameData.class, new IndexOrNameDataListener()).sheet().doRead();

综合应用

1: 环境搭建

2: 实现文件导入功能(采用异步上传并使用遮罩层)

   @RequestMapping("/upload")
    @ResponseBody
    public void upload(MultipartFile file, HttpServletResponse response) throws IOException {
        long t1 = System.currentTimeMillis();
        EasyExcel.read(file.getInputStream(), Employee.class, new EmployeeListener(service)).sheet().doRead();
        long t2 = System.currentTimeMillis();
       response.setContentType("text/html;charset=utf-8");//提示给前端
       response.getWriter().print("导入数据成功,共用时:"+(t2-t1));
    }

3: 实现文件导出功能(采用同步下载并解决文件名中文乱码)

@RequestMapping("/download")
    public void download(HttpServletResponse response) throws IOException {
// 这里注意 有同学反应使用swagger 会导致各种问题,请直接用浏览器或者用postman
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
        String fileName = URLEncoder.encode("测试666", "UTF-8").replaceAll("\\+", "%20");
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
        EasyExcel.write(response.getOutputStream(), Employee.class).sheet("模板").doWrite(service.getData());
    }

非常感谢您阅读到这里,创作不易!如果这篇文章对您有帮助,希望能留下您的点赞**👍 关注**💖 收藏 💕**评论💬感谢支持!!!**

听说 三连能够给人 带来好运!更有可能年入百w,进入大厂,上岸

相关文章
|
21天前
|
Java API Apache
Java编程如何读取Word文档里的Excel表格,并在保存文本内容时保留表格的样式?
【10月更文挑战第29天】Java编程如何读取Word文档里的Excel表格,并在保存文本内容时保留表格的样式?
89 5
|
21天前
|
存储 Java 编译器
Java内存模型(JMM)深度解析####
本文深入探讨了Java内存模型(JMM)的工作原理,旨在帮助开发者理解多线程环境下并发编程的挑战与解决方案。通过剖析JVM如何管理线程间的数据可见性、原子性和有序性问题,本文将揭示synchronized关键字背后的机制,并介绍volatile关键字和final关键字在保证变量同步与不可变性方面的作用。同时,文章还将讨论现代Java并发工具类如java.util.concurrent包中的核心组件,以及它们如何简化高效并发程序的设计。无论你是初学者还是有经验的开发者,本文都将为你提供宝贵的见解,助你在Java并发编程领域更进一步。 ####
|
2天前
|
Java
java内存区域
1)栈内存:保存所有的对象名称 2)堆内存:保存每个对象的具体属性 3)全局数据区:保存static类型的属性 4)全局代码区:保存所有的方法定义
8 1
|
16天前
|
缓存 算法 Java
本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制
在现代软件开发中,性能优化至关重要。本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制。通过调整垃圾回收器参数、优化堆大小与布局、使用对象池和缓存技术,开发者可显著提升应用性能和稳定性。
36 6
|
20天前
|
存储 缓存 安全
Java内存模型(JMM):深入理解并发编程的基石####
【10月更文挑战第29天】 本文作为一篇技术性文章,旨在深入探讨Java内存模型(JMM)的核心概念、工作原理及其在并发编程中的应用。我们将从JMM的基本定义出发,逐步剖析其如何通过happens-before原则、volatile关键字、synchronized关键字等机制,解决多线程环境下的数据可见性、原子性和有序性问题。不同于常规摘要的简述方式,本摘要将直接概述文章的核心内容,为读者提供一个清晰的学习路径。 ####
35 2
|
21天前
|
Java API Apache
|
21天前
|
存储 安全 Java
什么是 Java 的内存模型?
Java内存模型(Java Memory Model, JMM)是Java虚拟机(JVM)规范的一部分,它定义了一套规则,用于指导Java程序中变量的访问和内存交互方式。
49 1
|
24天前
|
存储 Java API
Java实现导出多个excel表打包到zip文件中,供客户端另存为窗口下载
Java实现导出多个excel表打包到zip文件中,供客户端另存为窗口下载
34 4
|
27天前
|
存储 运维 Java
💻Java零基础:深入了解Java内存机制
【10月更文挑战第18天】本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
30 1
|
30天前
|
监控 安全 Java
Java Z 垃圾收集器如何彻底改变内存管理
大家好,我是V哥。今天聊聊Java的ZGC(Z Garbage Collector)。ZGC是一个低延迟垃圾收集器,专为大内存应用场景设计。其核心优势包括:极低的暂停时间(通常低于10毫秒)、支持TB级内存、使用着色指针实现高效对象管理、并发压缩和去碎片化、不分代的内存管理。适用于实时数据分析、高性能服务器和在线交易系统等场景,能显著提升应用的性能和稳定性。如何启用?只需在JVM启动参数中加入`-XX:+UseZGC`即可。
145 0
下一篇
无影云桌面