课程介绍
本博客主要讲解在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,进入大厂,上岸