https://easyexcel.opensource.alibaba.com/docs/current/quickstart/write#web%E4%B8%AD%E7%9A%84%E5%86%99
Apache POI [1] 是用Java编写的免费开源的跨平台的 Java API,Apache POI提供API给Java程序对Microsoft Office格式档案读和写的功能。POI为“Poor Obfuscation Implementation”的首字母缩写,意为“简洁版的模糊实现”。
HSSF [1] - 提供读写Microsoft Excel XLS格式档案的功能。
XSSF [1] - 提供读写Microsoft Excel OOXML XLSX格式档案的功能。
HWPF [1] - 提供读写Microsoft Word DOC格式档案的功能。
HSLF [1] - 提供读写Microsoft PowerPoint格式档案的功能。
HDGF [1] - 提供读Microsoft Visio格式档案的功能。
HPBF [1] - 提供读Microsoft Publisher格式档案的功能。
HSMF [1] - 提供读Microsoft Outlook格式档案的功能。
Java领域解析、生成Excel比较有名的框架有Apache poi、jxl等。但他们都存在一个严重的问题就是非常的耗内存。如果你的系统并发量不大的话可能还行,但是一旦并发上来后一定会OOM或者JVM频繁的full gc。easyExcel是阿里巴巴开源的一个excel处理框架,以使用简单、节省内存著称。64M内存1分钟内读取75M(46W行25列)的Excel(当然还有急速模式能更快,但是内存占用会在100M多一点)。
easyExcel能大大减少占用内存的主要原因是在解析Excel时没有将文件数据一次性全部加载到内存中,而是从磁盘上一行行读取数据,逐个解析。下图是easyExcel和POI在解析Excel时的对比图。
easyExcel采用一行一行的解析模式,并将一行的解析结果以观察者的模式通知处理(AnalysisEventListener)。
1 基础版本
<!-- https://mvnrepository.com/artifact/org.apache.poi/poi 03 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.9</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.poi/poi-ooxml 07-->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.9</version>
</dependency>
<!-- https://mvnrepository.com/artifact/joda-time/joda-time -->
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.10.1</version>
</dependency>
HSSFWorkbook 03
XSSFWorkbook 07
SXSSFWorkbook 超级
package com.poi;
import cn.hutool.core.date.DateTime;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.FileOutputStream;
import java.io.IOException;
public class POITest {
public static void main(String[] args) throws IOException {
test03();
test07();
}
public static void test07() throws IOException {
// 创建一个工作簿
Workbook workbook = new XSSFWorkbook();
// 创建一个工作表
Sheet sheet = workbook.createSheet("sheet-1");
// 创建一行
Row row_0 = sheet.createRow(0); // 第一行
// 创建一个单元格
Cell cell_00 = row_0.createCell(0);
cell_00.setCellValue("今日新增观众"); // (0,0)
Cell cell_01 = row_0.createCell(1);
cell_01.setCellValue(666); // (0,1)
Row row_1 = sheet.createRow(1);
Cell cell_10 = row_1.createCell(0);
cell_10.setCellValue("统计时间"); // (1,0)
Cell cell_11 = row_1.createCell(1);
cell_11.setCellValue(new DateTime().toString("yyyy-MM-dd HH:mm:ss")); // (1,1)
// 生成一个表 IO
String Path = "/Users/zhaoshuai11/Desktop/utools/src/main/resources/file/";
FileOutputStream fileOutputStream = new FileOutputStream(Path + "观众统计表07.xlsx");
workbook.write(fileOutputStream);
fileOutputStream.close();
}
public static void test03() throws IOException {
// 创建一个工作簿
Workbook workbook = new HSSFWorkbook();
// 创建一个工作表
Sheet sheet = workbook.createSheet("sheet-1");
// 创建一行
Row row_0 = sheet.createRow(0); // 第一行
// 创建一个单元格
Cell cell_00 = row_0.createCell(0);
cell_00.setCellValue("今日新增观众"); // (0,0)
Cell cell_01 = row_0.createCell(1);
cell_01.setCellValue(666); // (0,1)
Row row_1 = sheet.createRow(1);
Cell cell_10 = row_1.createCell(0);
cell_10.setCellValue("统计时间"); // (1,0)
Cell cell_11 = row_1.createCell(1);
cell_11.setCellValue(new DateTime().toString("yyyy-MM-dd HH:mm:ss")); // (1,1)
// 生成一个表 IO
String Path = "/Users/zhaoshuai11/Desktop/utools/src/main/resources/file/";
FileOutputStream fileOutputStream = new FileOutputStream(Path + "观众统计表03.xls");
workbook.write(fileOutputStream);
fileOutputStream.close();
}
}
2 数据批量导入
大文件写HSSF,缺点:最多只能处理65536行,否则会抛出异常。
java. lang. IlegalArgumentException: Invalid row number (65536) outside al lowable range (0. .65535)
优点:过程中写入缓存,不操作磁盘,最后一次性写入磁盘,速度快。
大文件写XSSF,缺点:写数据时速度非常慢,非常耗内存,也会发生内存溢出,如100万条。
优点:可以写较大的数据量,如20万条。
大文件写SXSSF,优点:可以写非常大的数据量,如100万条甚至更多条,写数据速度快,占用更少的内存。
注意:过程中会产生临时文件,需要清理临时文件。
默认由100条记录被保存在内存中,如果超过这数量,则最前面的数据被写入临时文件。如果想自定义内存中数据的数量,可以使用new SXSSFWorkbook (数量)。
SXSSFWorkbook-来至官方的解释:实现BigGridDemo策略的流式XSSFWorkbook版本。这允许写入非常大的文件而不会耗尽內存,因为任何时候只有可配置的行部分被保存在内存中。请注意,仍然可能会消耗大量內存,这些内存基于您正在使用的功能,例如合并区域,注释……仍然只存储在内存中,因此如果广泛使用,可能需要大量内存。
1 HSSFWorkbook - 2.242s
public static void testHSSFWorkbookBD() throws IOException {
// 时间
long startTime = System.currentTimeMillis();
Workbook workbook = new HSSFWorkbook();
Sheet sheet = workbook.createSheet();
/**
* Exception in thread "main" java.lang.IllegalArgumentException: Invalid row number (65536) outside allowable range (0..65535)
*/
for (int i = 0; i < 65536; i++) {
Row row = sheet.createRow(i);
for (int j = 0; j < 10; j++) {
Cell cell = row.createCell(j);
cell.setCellValue(j);
}
}
System.out.println("over");
String Path = "/Users/zhaoshuai11/Desktop/utools/src/main/resources/file/";
FileOutputStream fileOutputStream = new FileOutputStream(Path + "HSSFWorkbook03.xls");
workbook.write(fileOutputStream);
fileOutputStream.close();
long endTime = System.currentTimeMillis();
System.out.println((double) (endTime - startTime) / 1000); // 时间比较快 数据量有限
}
2 XSSFWorkbook - 13.682s
public static void testXSSFWorkbookBD() throws IOException {
// 时间
long startTime = System.currentTimeMillis();
Workbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet();
for (int i = 0; i < 200000; i++) {
Row row = sheet.createRow(i);
for (int j = 0; j < 10; j++) {
Cell cell = row.createCell(j);
cell.setCellValue(j);
}
}
System.out.println("over");
String Path = "/Users/zhaoshuai11/Desktop/utools/src/main/resources/file/";
FileOutputStream fileOutputStream = new FileOutputStream(Path + "HSSFWorkbook07.xlsx");
workbook.write(fileOutputStream);
fileOutputStream.close();
long endTime = System.currentTimeMillis();
System.out.println((double) (endTime - startTime) / 1000); // 5.064 时间长 但是可以写入更加多的数据
}
3 SXSSFWorkbook - 5.788s
public static void testBDSXSSFWorkbook() throws IOException {
// 时间
long startTime = System.currentTimeMillis();
SXSSFWorkbook workbook = new SXSSFWorkbook();
Sheet sheet = workbook.createSheet();
for (int i = 0; i < 200000; i++) {
Row row = sheet.createRow(i);
for (int j = 0; j < 10; j++) {
Cell cell = row.createCell(j);
cell.setCellValue(j);
}
}
System.out.println("over");
String Path = "/Users/zhaoshuai11/Desktop/utools/src/main/resources/file/";
FileOutputStream fileOutputStream = new FileOutputStream(Path + "SXSSFWorkbook.xlsx");
workbook.write(fileOutputStream);
// 清除临时文件
workbook.dispose();
fileOutputStream.close();
long endTime = System.currentTimeMillis();
System.out.println((double) (endTime - startTime) / 1000);
}
3 POI读
1 基础版本- 注意获取值的类型
package com.poi;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.FileInputStream;
import java.io.IOException;
public class POIRead {
public static void main(String[] args) throws IOException {
test07();
}
public static void test03() throws IOException {
// 获取文件流
String PATH = "/Users/zhaoshuai11/Desktop/utools/src/main/resources/file/观众统计表03.xls";
FileInputStream fileInputStream = new FileInputStream(PATH);
// 创建一个工作簿
Workbook workbook = new HSSFWorkbook(fileInputStream);
Sheet sheet = workbook.getSheetAt(0);
Row row = sheet.getRow(0);
Cell cell = row.getCell(1);
/**
* 获取不同的类型
* System.out.println(cell.getStringCellValue());
*/
System.out.println(cell.getNumericCellValue());
fileInputStream.close();
}
public static void test07() throws IOException {
// 获取文件流
String PATH = "/Users/zhaoshuai11/Desktop/utools/src/main/resources/file/观众统计表07.xlsx";
FileInputStream fileInputStream = new FileInputStream(PATH);
// 创建一个工作簿
Workbook workbook = new XSSFWorkbook(fileInputStream);
Sheet sheet = workbook.getSheetAt(0);
Row row = sheet.getRow(0);
Cell cell = row.getCell(1);
/**
* 获取不同的类型
* System.out.println(cell.getStringCellValue());
*/
System.out.println(cell.getNumericCellValue());
fileInputStream.close();
}
}
2 读取不同类型的值
package com.poi;
public class POIRead {
public static void main(String[] args) throws IOException {
testCellType();
}
public static void testCellType() throws IOException {
// 获取文件流
String PATH = "/Users/zhaoshuai11/Desktop/utools/src/main/resources/file/住址.xlsx";
FileInputStream fileInputStream = new FileInputStream(PATH);
// 创建一个工作簿
Workbook workbook = new XSSFWorkbook(fileInputStream);
Sheet sheet = workbook.getSheetAt(0);
Row rowTitle = sheet.getRow(0);
if (null != rowTitle) {
int cells = rowTitle.getPhysicalNumberOfCells();
for (int i = 0; i < cells; i++) {
Cell cell = rowTitle.getCell(i);
if (null != cell) {
CellType cellType = cell.getCellType();
String cellValue = cell.getStringCellValue();
System.out.print(cellValue + " | ");
}
}
}
System.out.println();
int rows = sheet.getPhysicalNumberOfRows();
for (int i = 1; i < rows; i++) {
Row rowData = sheet.getRow(i);
if (null != rowData) {
int cells = rowData.getPhysicalNumberOfCells();
for (int j = 0; j < cells; j++) {
Cell cell = rowData.getCell(j);
if (null != cell) {
CellType cellType = cell.getCellType();
String cellValue = "";
// 匹配类型
switch (cellType) {
case STRING:
cellValue = cell.getStringCellValue();
break;
case BOOLEAN:
cellValue = String.valueOf(cell.getBooleanCellValue());
break;
case BLANK:
break;
case NUMERIC:
if (DateUtil.isCellDateFormatted(cell)) {
cellValue = new DateTime(cell.getDateCellValue()).toString("yyyy-MM-dd");
} else {
cellValue = new DecimalFormat("0").format(cell.getNumericCellValue());
}
break;
case ERROR:
break;
}
System.out.print(cellValue + " | ");
}
}
System.out.println();
}
}
fileInputStream.close();
}
public static void test03() throws IOException {
// 获取文件流
String PATH = "/Users/zhaoshuai11/Desktop/utools/src/main/resources/file/观众统计表03.xls";
FileInputStream fileInputStream = new FileInputStream(PATH);
// 创建一个工作簿
Workbook workbook = new HSSFWorkbook(fileInputStream);
Sheet sheet = workbook.getSheetAt(0);
Row row = sheet.getRow(0);
Cell cell = row.getCell(1);
/**
* 获取不同的类型
* System.out.println(cell.getStringCellValue());
*/
System.out.println(cell.getNumericCellValue());
fileInputStream.close();
}
public static void test07() throws IOException {
// 获取文件流
String PATH = "/Users/zhaoshuai11/Desktop/utools/src/main/resources/file/观众统计表07.xlsx";
FileInputStream fileInputStream = new FileInputStream(PATH);
// 创建一个工作簿
Workbook workbook = new XSSFWorkbook(fileInputStream);
Sheet sheet = workbook.getSheetAt(0);
Row row = sheet.getRow(0);
Cell cell = row.getCell(1);
/**
* 获取不同的类型
* System.out.println(cell.getStringCellValue());
*/
System.out.println(cell.getNumericCellValue());
fileInputStream.close();
}
}
4 easyEXCEL
<!-- https://mvnrepository.com/artifact/com.alibaba/easyexcel -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.0.5</version>
</dependency>
1 通用数据生成
private static List<DemoData> data() {
List<DemoData> list = ListUtils.newArrayList();
for (int i = 0; i < 10; i++) {
DemoData data = new DemoData();
data.setString("字符串" + i);
data.setDate(new Date());
data.setDoubleData(0.56);
list.add(data);
}
return list;
}
2 最简单的写
2.1 最简单的写的对象
package com.easyexcel;
@Data
@EqualsAndHashCode
public class DemoData {
@ExcelProperty("字符串标题")
private String string;
@ExcelProperty("日期标题")
private Date date;
@ExcelProperty("数字标题")
private Double doubleData;
/**
* 忽略这个字段
*/
@ExcelIgnore
private String ignore;
}
2.2 代码
/**
* 最简单的写
* <p>
* 1. 创建excel对应的实体对象 参照{@link DemoData}
* <p>
* 2. 直接写即可
*/
@Test
public void simpleWrite() {
// 注意 simpleWrite在数据量不大的情况下可以使用(5000以内,具体也要看实际情况),数据量大参照 重复多次写入
String fileName = "/Users/zhaoshuai11/Desktop/utools/src/main/resources/file/easyExcel.xlsx";
// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
// 如果这里想使用03 则 传入excelType参数即可
// 分页查询数据
EasyExcel.write(fileName, DemoData.class)
.sheet("模板")
.doWrite(TestEasyExcel::data);
}
3 根据参数只导出指定列
/**
* 根据参数只导出指定列
* <p>
* 1. 创建excel对应的实体对象 参照{@link DemoData}
* <p>
* 2. 根据自己或者排除自己需要的列
* <p>
* 3. 直接写即可
*
* @since 2.1.1
*/
@Test
public void excludeOrIncludeWrite() {
String fileName = "/Users/zhaoshuai11/Desktop/utools/src/main/resources/file/easyExcel-1.xlsx";
// 这里需要注意 在使用ExcelProperty注解的使用,如果想不空列则需要加入order字段,而不是index,order会忽略空列,然后继续往后,而index,不会忽略空列,在第几列就是第几列。
// 根据用户传入字段 假设我们要忽略 date
Set<String> excludeColumnFiledNames = new HashSet<String>();
excludeColumnFiledNames.add("date");
// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
EasyExcel.write(fileName, DemoData.class).excludeColumnFiledNames(excludeColumnFiledNames).sheet("模板")
.doWrite(data());
String fileName_2 = "/Users/zhaoshuai11/Desktop/utools/src/main/resources/file/easyExcel-2.xlsx";
// 根据用户传入字段 假设我们只要导出 date
Set<String> includeColumnFiledNames = new HashSet<String>();
includeColumnFiledNames.add("date");
// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
EasyExcel.write(fileName_2, DemoData.class).includeColumnFiledNames(includeColumnFiledNames).sheet("模板")
.doWrite(data());
}
4 指定写入的列
package com.easyexcel;
@Getter
@Setter
@EqualsAndHashCode
public class IndexData {
@ExcelProperty(value = "字符串标题", index = 0)
private String string;
@ExcelProperty(value = "日期标题", index = 1)
private Date date;
/**
* 这里设置3 会导致第二列空的
*/
@ExcelProperty(value = "数字标题", index = 3)
private Double doubleData;
}
/**
* 指定写入的列
* <p>1. 创建excel对应的实体对象 参照{@link IndexData}
* <p>2. 使用{@link ExcelProperty}注解指定写入的列
* <p>3. 直接写即可
*/
@Test
public void indexWrite() {
String fileName = "/Users/zhaoshuai11/Desktop/utools/src/main/resources/file/easyExcel-3.xlsx";
// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
EasyExcel.write(fileName, IndexData.class).sheet("模板").doWrite(data());
}
5 复杂头写入
package com.easyexcel;
@Getter
@Setter
@EqualsAndHashCode
public class ComplexHeadData {
@ExcelProperty({"主标题", "字符串标题"})
private String string;
@ExcelProperty({"主标题", "日期标题"})
private Date date;
@ExcelProperty({"主标题", "数字标题"})
private Double doubleData;
}
/**
* 复杂头写入
* <p>1. 创建excel对应的实体对象 参照{@link ComplexHeadData}
* <p>2. 使用{@link ExcelProperty}注解指定复杂的头
* <p>3. 直接写即可
*/
@Test
public void complexHeadWrite() {
String fileName = "/Users/zhaoshuai11/Desktop/utools/src/main/resources/file/easyExcel-4.xlsx";
// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
EasyExcel.write(fileName, ComplexHeadData.class).sheet("模板").doWrite(data());
}
6 重复多次写入(写到单个或者多个Sheet)
/**
* 重复多次写入
* 直接调用二次写入即可
*/
@Test
public void repeatedWrite() {
// 方法1: 如果写到同一个sheet
String fileName = "/Users/zhaoshuai11/Desktop/utools/src/main/resources/file/easyExcel.xlsx";
// 这里 需要指定写用哪个class去写
ExcelWriter excelWriter = EasyExcel.write(fileName, DemoData.class).build();
// 这里注意 如果同一个sheet只要创建一次
WriteSheet writeSheet = EasyExcel.writerSheet("模板").build();
// 去调用写入,这里我调用了五次,实际使用时根据数据库分页的总的页数来
for (int i = 0; i < 5; i++) {
// 分页去数据库查询数据 这里可以去数据库查询每一页的数据
List<DemoData> data = data();
excelWriter.write(data, writeSheet);
}
excelWriter.finish();
}
// 方法2: 如果写到不同的sheet 同一个对象
ExcelWriter excelWriter = EasyExcel.write(fileName, DemoData.class).build();
// 去调用写入,这里我调用了五次,实际使用时根据数据库分页的总的页数来。这里最终会写到5个sheet里面
for (int i = 0; i < 5; i++) {
// 每次都要创建writeSheet 这里注意必须指定sheetNo 而且sheetName必须不一样
WriteSheet writeSheet = EasyExcel.writerSheet(i, "模板" + i).build();
// 分页去数据库查询数据 这里可以去数据库查询每一页的数据
List<DemoData> data = data();
excelWriter.write(data, writeSheet);
}
excelWriter.finish();
7 日期、数字或者自定义格式转换
package com.easyexcel;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.format.DateTimeFormat;
import com.alibaba.excel.annotation.format.NumberFormat;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import java.util.Date;
@Getter
@Setter
@EqualsAndHashCode
public class ConverterData {
/**
* 我想所有的 字符串起前面加上"自定义:"三个字
*/
@ExcelProperty(value = "字符串标题")
private String string;
/**
* 我想写到excel 用年月日的格式
*/
@DateTimeFormat("yyyy年MM月dd日HH时mm分ss秒")
@ExcelProperty("日期标题")
private Date date;
/**
* 我想写到excel 用百分比表示
*/
@NumberFormat("#.##%")
@ExcelProperty(value = "数字标题")
private Double doubleData;
}
/**
* 日期、数字或者自定义格式转换
* <p>1. 创建excel对应的实体对象 参照{@link ConverterData}
* <p>2. 使用{@link ExcelProperty}配合使用注解{@link DateTimeFormat}、{@link NumberFormat}或者自定义注解
* <p>3. 直接写即可
*/
@Test
public void converterWrite() {
String fileName = "/Users/zhaoshuai11/Desktop/utools/src/main/resources/file/easyExcel.xlsx";
// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
EasyExcel.write(fileName, ConverterData.class).sheet("模板").doWrite(data());
}
8 最简单的读
1 最简单的读的监听器
package com.easyexcel;
// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
@Slf4j
public class DemoDataListener implements ReadListener<DemoData> {
/**
* 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收
*/
private static final int BATCH_COUNT = 100;
/**
* 缓存的数据
*/
private List<DemoData> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
/**
* 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。
*/
private DemoDAO demoDAO;
public DemoDataListener() {
// 这里是demo,所以随便new一个。实际使用如果到了spring,请使用下面的有参构造函数
demoDAO = new DemoDAO();
}
/**
* 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
*
* @param demoDAO
*/
public DemoDataListener(DemoDAO demoDAO) {
this.demoDAO = demoDAO;
}
/**
* 这个每一条数据解析都会来调用
*
* @param data one row value. Is is same as {@link AnalysisContext#readRowHolder()}
* @param context
*/
@Override
public void invoke(DemoData data, AnalysisContext context) {
log.info("解析到一条数据:{}", JSON.toJSONString(data));
cachedDataList.add(data);
// 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
if (cachedDataList.size() >= BATCH_COUNT) {
saveData();
// 存储完成清理 list
cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
}
}
/**
* 所有数据解析完成了 都会来调用
*
* @param context
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 这里也要保存数据,确保最后遗留的数据也存储到数据库
saveData();
log.info("所有数据解析完成!");
}
/**
* 加上存储数据库
*/
private void saveData() {
log.info("{}条数据,开始存储数据库!", cachedDataList.size());
demoDAO.save(cachedDataList);
log.info("存储数据库成功!");
}
}
2 持久层
package com.easyexcel;
/**
* 假设这个是你的DAO存储。当然还要这个类让spring管理,当然你不用需要存储,也不需要这个类。
**/
public class DemoDAO {
public void save(List<DemoData> list) {
// 持久化操作
// 如果是mybatis,尽量别直接调用多次insert,自己写一个mapper里面新增一个方法batchInsert,所有数据一次性插入
}
}
3 代码
/**
* 最简单的读
* <p>
* 1. 创建excel对应的实体对象 参照{@link DemoData}
* <p>
* 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoDataListener}
* <p>
* 3. 直接读即可
*/
@Test
public void simpleRead() {
// 写法1:JDK8+ ,不用额外写一个DemoDataListener
// since: 3.0.0-beta1
String fileName = "/Users/zhaoshuai11/Desktop/utools/src/main/resources/file/easyExcel.xlsx";
// 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭
// 这里每次会读取100条数据 然后返回过来 直接调用使用数据就行
EasyExcel.read(fileName, DemoData.class, new PageReadListener<DemoData>(dataList -> {
for (DemoData demoData : dataList) {
log.info("读取到一条数据{}", JSON.toJSONString(demoData));
}
})).sheet().doRead();
}
// 写法2:
// 匿名内部类 不用额外写一个DemoDataListener
// 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭
read(fileName, DemoData.class, new ReadListener<DemoData>() {
/**
* 单次缓存的数据量
*/
public static final int BATCH_COUNT = 100;
/**
*临时存储
*/
private List<DemoData> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
@Override
public void invoke(DemoData data, AnalysisContext context) {
cachedDataList.add(data);
if (cachedDataList.size() >= BATCH_COUNT) {
saveData();
// 存储完成清理 list
cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
}
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
saveData();
}
/**
* 加上存储数据库
*/
private void saveData() {
log.info("{}条数据,开始存储数据库!", cachedDataList.size());
log.info("存储数据库成功!");
}
}).sheet().doRead();
文章来源:https://www.toymoban.com/news/detail-451145.html
// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
// 写法3:
// 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭
EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet().doRead();
文章来源地址https://www.toymoban.com/news/detail-451145.html
// 写法4
// 一个文件一个reader
ExcelReader excelReader = EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).build();
// 构建一个sheet 这里可以指定名字或者no
ReadSheet readSheet = EasyExcel.readSheet(0).build();
// 读取一个sheet
excelReader.read(readSheet);
excelReader.finish();
到了这里,关于JAVA-POI && easyEXCEL的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!