文章目录
- 一、前言
- 二、POI 和 easyExcel
- 1.1概念
- 1.2xls和xlsx区别
- 1.3文档
- 1.4Excel概念
- 二、常用场景
- 三、内存问题
- 四、POI的使用
- 4.1创建一个空项目
- 4.2引入pom依赖
- 4.3workbook的三个实现类
- 4.4写的实现
- 4.4读的实现
- 4.5POI常用的包
- 五、EasyExcel的使用
- 5.1创建一个空项目
- 5.2引入pom依赖
- 5.3写的实现
- 5.4读的实现
一、前言
在日常的开发中,做报表开发的时候,经常会用到Excel导入数据或者导出数据到Excel里,在java中,通常用到第三方提供的技术来进行Excel文件的读写。
第三方提供的有:Apache POI、JXL、Alibaba EasyExcel。
下面介绍Apache POI和Alibaba EasyExcel。
二、POI 和 easyExcel
1.1概念
apache POI:开放源码式函数库。POI提供API给java程序堆Microsoft office格式档案读和写的功能。比较复杂。
Alibaba easyExcel:Java解析Excel工具,对POI做出优化,比较简单使用。
1.2xls和xlsx区别
xls(03)最大行数:65536。
xlsx(07) 最大行数:无限。
1.3文档
POI文档:https://poi.apache.org/
easyExcel官方文档:https://www.yuque.com/easyexcel/doc
1.4Excel概念
1.工作簿:
2.工作表:
3.行:
4.列:
5.单元格:3和4组成
二、常用场景
-
将用户信息导出为excel表格(导出数据)
-
将本地Excel表中的数据上传到服务器里(数据上传)
三、内存问题
POI:100w数据先加载到内存(会oom),再一次性写入文件。
EasyExcel:读取通过文件形式,一行一行的解析 ,并将一行的解析结果以观察者的模式通知处理(AnalysisEventListener)。 。
四、POI的使用
4.1创建一个空项目
4.2引入pom依赖
<!-- poi开始-->
<!-- xls-03-->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>4.1.2</version>
</dependency>
<!-- xls-07-->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.2</version>
</dependency>
<!-- poi结束-->
<!-- 日期格式化工具-->
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.10.4</version>
</dependency>
<!-- test-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
</dependency>
4.3workbook的三个实现类
4.4写的实现
生成 Excel文件步骤:
-
创建WorkBook,一个WorkBook代表一个Excel文件
-
以输出流的形式创建出Excel文件
-
调用createSheet(0)创建工作簿
-
调用createRow(0)创建行
-
调用createCell(0)创建单元格
-
调用setCellValue()完成对单元格内容的写入
-
调用write()方法,将Workbook对象中包含的数据,通过输出流,生成Excel文件
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.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.joda.time.DateTime;
import org.junit.Test;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* @Author: shen
* @CreateTime: 2022-01-03 22:47
* @Description: poi写
*/
public class ExcelWriteTest {
@Test
public void Test1() throws IOException {
//文件路径
String filePath="C:\\Users\\Administrator\\Desktop\\excel文件\\";
//excel名称
String excelName03 = "xiaoshen数据统计表03.xls";
String excelName07 = "xiaoshen数据统计表07.xlsx";
//sheet页名称
String sheetName = "xiaoshen数据统计表";
//1.创建工作簿:03 xls
Workbook workbook = new HSSFWorkbook();
//1.创建工作簿:07 xlsx
Workbook workbook1 = new XSSFWorkbook();
//1.升级版创建工作簿:07 xlsx
Workbook workbook2 = new SXSSFWorkbook();
//2.通过文件输出流生成excel文件
FileOutputStream fileOutputStream = new FileOutputStream(filePath + excelName03);
//new ExcelWriteTest().testWrite07BigData(workbook,sheetName,fileOutputStream);
new ExcelWriteTest().testWrite03(workbook,sheetName,fileOutputStream);
}
private void testWrite03 (Workbook workbook,String sheetName,FileOutputStream fileOutputStream)throws IOException{
//2.创建一个工作表
Sheet sheet = workbook.createSheet(sheetName);
//3.创建行
Row row1 = sheet.createRow(0);
//4.创建一个单元格
Cell cell11 = row1.createCell(0);
cell11.setCellValue("今日学习数量");
Cell cell12 = row1.createCell(1);
cell12.setCellValue(666);
//第二行
Row row2 = sheet.createRow(1);
Cell cell21 = row2.createCell(0);
cell21.setCellValue("时间");
Cell cell22 = row2.createCell(1);
String time = new DateTime().toString("yyyy-MM-dd HH:mm:ss");
cell22.setCellValue(time);
//输出
workbook.write(fileOutputStream);
//关闭流
fileOutputStream.flush();
fileOutputStream.close();
System.out.println("xiaoshen统计表o3生成完毕!");
}
public void testWrite07BigData (Workbook workbook,String sheetName,FileOutputStream fileOutputStream)throws IOException{
//时间
long start = System.currentTimeMillis();
Sheet sheet = workbook.createSheet(sheetName);
//写入数据
for (int rownum = 0; rownum < 65536; rownum++) {
Row row = sheet.createRow(rownum);
for (int cellNum = 0; cellNum < 10; cellNum++) {
Cell cell = row.createCell(cellNum);
cell.setCellValue(cellNum);
}
}
System.out.println("over");
//输出
workbook.write(fileOutputStream);
//关闭流
fileOutputStream.flush();
fileOutputStream.close();
//清除临时文件
((SXSSFWorkbook)workbook).dispose();
System.out.println("xiaoshen统计表o3生成完毕!");
long end = System.currentTimeMillis();
System.out.println("执行时间:"+(end-start)/1000 +"分");
}
}
4.4读的实现
读取Excel 步骤:
-
以输入流的形式获取到excel文件
-
创建WorkBook,传入该输入流
-
调用getSheetAt(0),获取到工作簿
-
调用getRow()获取到行
-
getCell()获取到单元格
-
调用getStringCellValue()获取到String的类型的值,调用getNumericCellValue()获取到double类型的值
import org.apache.poi.hssf.usermodel.HSSFDateUtil;
import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.joda.time.DateTime;
import org.junit.Test;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Date;
/**
* @Author: shen
* @CreateTime: 2022-01-03 22:47
* @Description: poi读
*/
public class ExcelReadTest {
@Test
public void test1() throws IOException {
//文件路径
String filePath="C:\\Users\\Administrator\\Desktop\\excel文件\\";
//excel名称:03
String excelName="xiaoshen数据统计表03.xls";
//创建文件输入流对象
FileInputStream fileInputStream = new FileInputStream(filePath + excelName);
//文件输入流读取excel
//new ExcelReadTest().testCellType03(fileInputStream);
new ExcelReadTest().testRead03(fileInputStream);
}
private void testRead03 (FileInputStream fileInputStream)throws IOException{
//读取流中的工作簿:03-xls
//XSSFWorkbook-07-xlsx、SXSSFWorkbook-07-xlsx
Workbook workbook = new HSSFWorkbook(fileInputStream);
//获取第一张工作表
Sheet sheet = workbook.getSheetAt(0);
//获取第一行
Row row = sheet.getRow(0);
//获取第一行的第一列单元格
Cell cell = row.getCell(0);
//注意:cell.getStringCellValue()获取字符串
//日常开发中的问题:需要判断不同类型的数据
System.out.println(cell.getStringCellValue());
Cell cell1 = row.getCell(1);
System.out.println(cell1.getNumericCellValue());
//关闭流
fileInputStream.close();
}
private void testCellType03 (FileInputStream fileInputStream)throws IOException{
//读取流中的工作簿
Workbook workbook = new HSSFWorkbook(fileInputStream);
//获取工作表
Sheet sheet = workbook.getSheetAt(0);
//获取总行数
int rowCount = sheet.getPhysicalNumberOfRows();
for (int rowNum = 0; rowNum < rowCount; rowNum++) {
Row row = sheet.getRow(rowNum);
if (row != null ){
//第一行有多少列
int cellCount = row.getPhysicalNumberOfCells();
for (int cellNum = 0; cellNum < cellCount; cellNum++) {
Cell cell = row.getCell(cellNum);
if (cell != null){
//匹配列的数据类型
CellType cellType = cell.getCellType();
switch (cellType){
case STRING://字符串
System.out.print(cell.getStringCellValue());
break;
case NUMERIC://数字:日期或者数字
if (HSSFDateUtil.isCellDateFormatted(cell)){//日期
Date date = cell.getDateCellValue();
System.out.print(new DateTime(date).toString());
}else {//数字
cell.setCellType(CellType.STRING);
System.out.print(cell.toString());
}
break;
case BOOLEAN://布尔
System.out.print(cell.getBooleanCellValue());
break;
case BLANK://空白
break;
case FORMULA://公式
//拿到计算公式
FormulaEvaluator formulaEvaluator = new HSSFFormulaEvaluator((HSSFWorkbook) workbook);
String cellFormula = cell.getCellFormula();
//计算
CellValue cellValue = formulaEvaluator.evaluate(cell);
String value = cellValue.formatAsString();
System.out.println(value);
break;
case ERROR:
System.out.print("错误类型!");
break;
default:
}
}
}
System.out.println();
}
}
//关闭流
fileInputStream.close();
}
}
4.5POI常用的包
HSSF - 提供读写Microsoft Excel XLS格式档案的功能。
XSSF - 提供读写Microsoft Excel OOXML XLSX格式档案的功能。
HWPF - 提供读写Microsoft Word DOC格式档案的功能。
HSLF - 提供读写Microsoft PowerPoint格式档案的功能。
HDGF - 提供读Microsoft Visio格式档案的功能。
HPBF - 提供读Microsoft Publisher格式档案的功能。
HSMF - 提供读Microsoft Outlook格式档案的功能。
五、EasyExcel的使用
github地址:https://github.com/alibaba/easyexcel
easyExcel地址:https://easyexcel.opensource.alibaba.com/docs/current/
5.1创建一个空项目
5.2引入pom依赖
<!-- easyExcel-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.1.3</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
<scope>compile</scope>
</dependency>
<!--解决log4j冲突问题-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-nop</artifactId>
<version>1.7.2</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.83</version>
</dependency>
<!-- test-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
</dependency>
5.3写的实现
生成excel文件使用的是EasyExcel中的write()方式,需要的是文件路径,和写入内容的类型。下面演示到的类型为:DemoData类型。
DemoData类:
import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.format.DateTimeFormat;
import com.alibaba.excel.annotation.format.NumberFormat;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.alibaba.excel.annotation.write.style.ContentRowHeight;
import com.alibaba.excel.metadata.data.WriteCellData;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import java.io.File;
import java.io.InputStream;
import java.net.URL;
import java.util.Date;
/**
* @Author: shen
* @CreateTime: 2022-01-04 11:36
* @Description: easyExcel实体类
*/
@Getter
@Setter
@EqualsAndHashCode
@ContentRowHeight(100)//图片高度
@ColumnWidth(100 / 8)//图片宽度
public class DemoData {
private File file;
private InputStream inputStream;
private byte[] byteArray;
/**
* 字符串起前面加上"自定义:"三个字:注解上使用converter属性即可
* @ExcelProperty()代表列头单元格的内容
*/
@ExcelProperty(value = "字符串标题",index = 0)
private String string;
@ExcelProperty(value = "日期标题",index = 1)
@DateTimeFormat("yyyy年MM月dd日HH时mm分ss秒")
private Date date;
@ExcelProperty(value = "数字标题",index = 2)
@NumberFormat("¥#,###")//注解@NumberFormat()代表单元格格式
private Double doubleData;
/**
* 忽略这个字段
*/
@ExcelIgnore
private String ignore;
/**
* 根据url导出
*/
private URL url;
/**
* 根据文件导出 并设置导出的位置。
*/
private WriteCellData<Void> writeCellDataFile;
/**
*超链接
*/
private WriteCellData<String> hyperlink;
/**
* 备注
*/
private WriteCellData<String> commentData;
/**
* 公式
*/
private WriteCellData<String> formulaData;
/**
* 指定单元格的样式。当然样式 也可以用注解等方式。
*/
private WriteCellData<String> writeCellStyle;
}
EasyexcelWriteTest类:
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.data.*;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.util.FileUtils;
import com.alibaba.excel.util.ListUtils;
import com.alibaba.excel.util.MapUtils;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.fastjson.JSON;
import org.apache.poi.ss.usermodel.FillPatternType;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.junit.Test;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URL;
import java.net.URLEncoder;
import java.util.*;
/**
* @Author: shen
* @CreateTime: 2022-01-04 11:40
* @Description: Easyexcel写入测试
*/
public class EasyexcelWriteTest {
String filePath="C:\\Users\\Administrator\\Desktop\\excel文件" + File.separator +"simpleWrite.xlsx";
String imagePath = "C:\\Users\\Administrator\\Desktop\\image" + File.separator + "1.png";
@Test
public void simpleWrite() {
new EasyexcelWriteTest().Write1(filePath);//简单写入excel
//new EasyexcelWriteTest().Write2(filePath);//根据不同的对象写入excel
}
@Test
public void imageWrite() {
//try-with-resource
try(InputStream inputStream = new FileInputStream(new File(imagePath))) {
ArrayList<DemoData> list = ListUtils.newArrayList();
DemoData demoData = new DemoData();
//放入图片类型,实际开发只要选择一种
//1
demoData.setByteArray(FileUtils.readFileToByteArray(new File(imagePath)));
//2
demoData.setFile(new File(imagePath));
//3
demoData.setString(imagePath);
//4
demoData.setInputStream(inputStream);
//5
//demoData.setUrl(new URL( "https://raw.githubusercontent.com/alibaba/easyexcel/master/src/test/resources/converter/img.jpg"));
//这里将随机选择一种方式:
WriteCellData<Void> imageWriteCellData = new WriteCellData<>();
imageWriteCellData.setType(CellDataTypeEnum.STRING);
imageWriteCellData.setStringValue("额外放一些文字");
//放入图片:ImageData是alibaba提供的
List<ImageData> imageList = new ArrayList<>();
ImageData imageData = new ImageData();
//放入二进制图片
imageData.setImage(FileUtils.readFileToByteArray(new File(imagePath)));
//设置图片类型
imageData.setImageType(ImageData.ImageType.PICTURE_TYPE_PNG);
//设置图片的上下左右
imageData.setTop(5);
imageData.setBottom(5);
imageData.setLeft(5);
imageData.setRight(40);
//设置图片的位置:起点相对于单元格为0
imageData.setRelativeFirstRowIndex(0);
imageData.setRelativeFirstColumnIndex(0);
imageData.setRelativeLastColumnIndex(1);
imageList.add(imageData);
//writeCellData设置图片数据
imageWriteCellData.setImageDataList(imageList);
//设置超链接
WriteCellData<String> hyperlinkWriteCellData = new WriteCellData<>("百度网站");
HyperlinkData hyperlinkData = new HyperlinkData();
hyperlinkData.setAddress("https://www.baidu.com");
hyperlinkData.setHyperlinkType(HyperlinkData.HyperlinkType.URL);
hyperlinkWriteCellData.setHyperlinkData(hyperlinkData);
demoData.setHyperlink(hyperlinkWriteCellData);
//设置备注:备注xiaoshen
WriteCellData<String> commentWriteCellData = new WriteCellData<>("备注的单元格");
CommentData commentData = new CommentData();
commentData.setAuthor("xiao shen");
commentData.setRichTextStringData(new RichTextStringData("这是一个备注"));
commentWriteCellData.setCommentData(commentData);
demoData.setCommentData(commentWriteCellData);
//设置公式:替换字符串中的1为2
WriteCellData<String> formulaWriteCellData = new WriteCellData<>();
FormulaData formulaData = new FormulaData();
formulaData.setFormulaValue("REPLACE(123456789,1,1,2)");
formulaWriteCellData.setFormulaData(formulaData);
demoData.setFormulaData(formulaWriteCellData);
//设置单元格样式:背景绿色、字符串
WriteCellData<String> styleWriteCellData = new WriteCellData<>("单元格样式");
WriteCellStyle writeCellStyle = new WriteCellStyle();
writeCellStyle.setFillBackgroundColor(IndexedColors.GREEN.getIndex());
writeCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND);
styleWriteCellData.setWriteCellStyle(writeCellStyle);
styleWriteCellData.setType(CellDataTypeEnum.STRING);
demoData.setWriteCellStyle(styleWriteCellData);
list.add(demoData);
//写入数据
EasyExcel.write(filePath,DemoData.class).sheet(0).doWrite(list);
System.out.println("写入完成!");
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* @author: shen
* @date: 2022/01/12 0:40
* @description: web中的写
* @param response
* @return:
**/
//@GetMapping("downloadFailedUsingJson")
public void webWrite(HttpServletResponse response) throws IOException {
try {
// 这里注意 有同学反应使用swagger 会导致各种问题,请直接用浏览器或者用postman
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
// 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
String fileName = URLEncoder
.encode("测试", "UTF-8")
.replaceAll("\\+", "%20");
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
//设置不关闭流
EasyExcel.write(response.getOutputStream(), DemoData.class)
.autoCloseStream(Boolean.FALSE)
.sheet("模板")
.doWrite(data());
}catch (Exception e){
// 重置response
response.reset();
response.setContentType("application/json");
response.setCharacterEncoding("utf-8");
Map<String, String> map = MapUtils.newHashMap();
map.put("status", "failure");
map.put("message", "下载文件失败" + e.getMessage());
response.getWriter().println(JSON.toJSONString(map));
}
}
/*
* @author: shen
* @date: 2022/01/05 10:18
* @description:写入数据方法
* @param: fileName :文件路径
* @return: void
**/
private void Write1(String fileName){
// 写法1 JDK8+
HashSet<String> set = new HashSet<>();
set.add("date");
//EasyExcel写入数据
EasyExcel.write(fileName, DemoData.class)// 指定以DemoData.class去写到fileName文件路径下。
.excelType(ExcelTypeEnum.XLSX)// Excel的格式:xlsx
.sheet(0,"模板")// sheet页的名称为模板
.excludeColumnFieldNames(set)//忽略set集合的字段
.head(head())//动态插入头部标题
.doWrite(() -> {//写入的数据
return data();//这里可以去数据查询
});
}
private void Write2(String fileName){
// 写法2
// 这里 需要指定写用哪个class去写
try (ExcelWriter excelWriter = EasyExcel.write(fileName, DemoData.class).build()) {
for (int i = 0; i < 5; i++) {
// 每次都要创建writeSheet 这里注意必须指定sheetNo 而且sheetName必须不一样
//每个sheet页存的对象不同。
WriteSheet writeSheet = EasyExcel.writerSheet(i,"模板"+i)
.head(DemoData2.class)
.build();
excelWriter.write(data(), writeSheet);
}
}
}
/*
* @author: shen
* @date: 2022/01/5 10:14
* @description:通用数据生成方法
* @param:
* @return: java.util.List<com.shenxm.easyexcel.utils.DemoData>
**/
private 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;
}
private List<List<String>> head() {
List<List<String>> list = new ArrayList<List<String>>();
List<String> head0 = new ArrayList<String>();
head0.add("字符串" + System.currentTimeMillis());
List<String> head1 = new ArrayList<String>();
head1.add("数字" + System.currentTimeMillis());
List<String> head2 = new ArrayList<String>();
head2.add("日期" + System.currentTimeMillis());
list.add(head0);
list.add(head1);
list.add(head2);
return list;
}
}
5.4读的实现
DemoData类: 提供读取数据的对象类
import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.format.DateTimeFormat;
import com.alibaba.excel.annotation.format.NumberFormat;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.alibaba.excel.annotation.write.style.ContentRowHeight;
import com.alibaba.excel.metadata.data.WriteCellData;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import java.io.File;
import java.io.InputStream;
import java.net.URL;
import java.util.Date;
/**
* @Author: shen
* @CreateTime: 2022-01-04 11:36
* @Description: easyExcel实体类
*/
@Getter
@Setter
@EqualsAndHashCode
@ContentRowHeight(100)//图片高度
@ColumnWidth(100 / 8)//图片宽度
public class DemoData {
private File file;
private InputStream inputStream;
private byte[] byteArray;
/**
* 字符串起前面加上"自定义:"三个字:注解上使用converter属性即可
* @ExcelProperty()代表列头单元格的内容
*/
@ExcelProperty(value = "字符串标题",index = 0)
private String string;
@ExcelProperty(value = "日期标题",index = 1)
@DateTimeFormat("yyyy年MM月dd日HH时mm分ss秒")
private Date date;
@ExcelProperty(value = "数字标题",index = 2)
@NumberFormat("¥#,###")//注解@NumberFormat()代表单元格格式
private Double doubleData;
/**
* 忽略这个字段
*/
@ExcelIgnore
private String ignore;
/**
* 根据url导出
*/
private URL url;
/**
* 根据文件导出 并设置导出的位置。
*/
private WriteCellData<Void> writeCellDataFile;
/**
*超链接
*/
private WriteCellData<String> hyperlink;
/**
* 备注
*/
private WriteCellData<String> commentData;
/**
* 公式
*/
private WriteCellData<String> formulaData;
/**
* 指定单元格的样式。当然样式 也可以用注解等方式。
*/
private WriteCellData<String> writeCellStyle;
}
DemoDataListener类: 读的监听器:DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.util.ListUtils;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import java.util.List;
/**
* @Author: shen
* @CreateTime: 2022-02-04 23:06
* @Description: 简单的读监听器
*/
// 有个很重要的点 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) {
System.out.println("解析到一条数据:" + JSON.toJSONString(data));
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("存储数据库成功!");
}
}