课程介绍
本博客主要讲解在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>
AI 代码解读
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()); } }
AI 代码解读
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; }
AI 代码解读
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)
AI 代码解读
路径改成这种才行 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; } }
AI 代码解读
核心代码:
EasyExcel.write(fileName, DemoData.class).sheet("测试").doWrite(data(100000));
AI 代码解读
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(); } }
AI 代码解读
核心代码:
EasyExcel.read(fileName, DemoData.class, new PageReadListener<DemoData> (list -> System.out.println(list))).sheet().doRead();
AI 代码解读
进阶操作
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); } } }
AI 代码解读
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); } }
AI 代码解读
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); } } }
AI 代码解读
EasyExcel.write(fileName).withTemplate(templateFileName).sheet().doFill(fillData);
AI 代码解读
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); }
AI 代码解读
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); } } }
AI 代码解读
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); } }
AI 代码解读
EasyExcel.read(fileName, IndexOrNameData.class, new IndexOrNameDataListener()).sheet().doRead();
AI 代码解读
综合应用
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)); }
AI 代码解读
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()); }
AI 代码解读
非常感谢您阅读到这里,创作不易!如果这篇文章对您有帮助,希望能留下您的点赞**👍 关注**💖 收藏 💕**评论💬感谢支持!!!**
听说 三连能够给人 带来好运!更有可能年入百w,进入大厂,上岸