EasyExcel
Excel介绍:
excel的样例:
工作簿:代表这个excel。
工作表:Sheet代表每一张表格
行属性:每一行的数据
列属性:每一列的数据
单元格:最小的属性
写入文件
03年excel和07的区别
07年使用XSSFWorkbook(),生成的Excel后缀名为xlsx
03年使用HSSFWorkbook(),生成的Excel后缀名为xls
少量数据操作:
public class Sheettest {
String PATH = "E:\\OAsystem\\OfficeAutomatic-System-master\\OfficeAutomatic-System-master";
@Test
public void testWriter03() throws Exception {
//1、创建一个工作簿
Workbook workbook = new HSSFWorkbook();
//2、创建一个工作表
Sheet sheet=workbook.createSheet("狂神观众统计表");
//3、创建一行.其中0代表第一行 创建了一个(1,1)单元格
Row row1 = sheet.createRow(0);
//4、创建一个单元格
Cell cell11 = row1.createCell(0);
//5、写入数据
cell11.setCellValue("今日新增观众");
//代表第一列的第二个数据
Cell cell12=row1.createCell(1);
cell12.setCellValue(666);
//第二行数据(2,1)
Row row2=sheet.createRow(1);
Cell cell21 = row2.createCell(0);
cell21.setCellValue("统计时间");
//(2,2)
Cell cell22 = row2.createCell(1);
String time = new DateTime().toString("yyyy-MM--dd HH:mm:ss");
cell22.setCellValue(time);
//生成一张表的信息(IO流) 03 版本使用的xls结尾
FileOutputStream fileOutputStream=new FileOutputStream(PATH + "狂神观众统计表03.xls");
//将数据输出到文件中
workbook.write(fileOutputStream);
//将其关闭
fileOutputStream.close();
System.out.println("输出完毕!");
}
@Test
public void testWriter07() throws Exception {
//1、创建一个工作簿 07年之后的是XSSF的格式
Workbook workbook = new XSSFWorkbook();
//2、创建一个工作表
Sheet sheet=workbook.createSheet("狂神观众统计表");
//3、创建一行.其中0代表第一行 创建了一个(1,1)单元格
Row row1 = sheet.createRow(0);
//4、创建一个单元格
Cell cell11 = row1.createCell(0);
//5、写入数据
cell11.setCellValue("今日新增观众");
//代表第一列的第二个数据
Cell cell12=row1.createCell(1);
cell12.setCellValue(666);
//第二行数据(2,1)
Row row2=sheet.createRow(1);
Cell cell21 = row2.createCell(0);
cell21.setCellValue("统计时间");
//(2,2)
Cell cell22 = row2.createCell(1);
String time = new DateTime().toString("yyyy-MM--dd HH:mm:ss");
cell22.setCellValue(time);
//生成一张表的信息(IO流) 03 版本使用的xls结尾
FileOutputStream fileOutputStream=new FileOutputStream(PATH + "狂神观众统计表03.xlsx");
//将数据输出到文件中
workbook.write(fileOutputStream);
//将其关闭
fileOutputStream.close();s
System.out.println("输出完毕!");
}
}
大量数据操作
其中使用HSSF来操作:
public void testWriter03bigdata() throws IOException {
//开始时间
long begin = System.currentTimeMillis();
//创建一个薄
Workbook workbook=new HSSFWorkbook();
//创建一个表
Sheet sheet=workbook.createSheet();
//写入数据
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");
FileOutputStream outputStream = new FileOutputStream(PATH+"testWriter03bigdata.xls");
workbook.write(outputStream);
outputStream.close();
//结束时间
long end = System.currentTimeMillis();
System.out.println((double)(end-begin)/1000);
}
注:当其中的数据超过了65536行的之后会报出异常:
我们会选择使用07版的excel来进行操作。
其中使用XSSF来操作:
缺点:写数据时速度非常慢,非常耗内存也会造成内存溢出
优点:可以写入大量的数据。
public void testWriter07bigdata() throws IOException {
//开始时间
long begin = System.currentTimeMillis();
//创建一个薄
Workbook workbook=new XSSFWorkbook();
//创建一个表
Sheet sheet=workbook.createSheet();
//写入数据
for (int rowNum = 0;rowNum<100000;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");
FileOutputStream outputStream = new FileOutputStream(PATH+"testWriter07bigdata1.xlsx");
workbook.write(outputStream);
outputStream.close();
//结束时间
long end = System.currentTimeMillis();
System.out.println((double)(end-begin)/1000);
}
大文件书写使用SXSSF提高效率:
使用SXSSFWorkbook()来进行写入,其中会产生一个临时文件,使用一下将临时文件关闭。
//清楚临时文件
((SXSSFWorkbook) workbook).dispose();
public void testWriter07bigdataS() throws IOException {
//开始时间
long begin = System.currentTimeMillis();
//创建一个薄
Workbook workbook=new SXSSFWorkbook();
//创建一个表
Sheet sheet=workbook.createSheet();
//写入数据
for (int rowNum = 0;rowNum<100000;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");
FileOutputStream outputStream = new FileOutputStream(PATH+"testWriter07bigdataS.xlsx");
workbook.write(outputStream);
outputStream.close();
//清楚临时文件
((SXSSFWorkbook) workbook).dispose();
//结束时间
long end = System.currentTimeMillis();
System.out.println((double)(end-begin)/1000);
}
读取文件
03版本读取文件:
public void testWriter03() throws Exception {
//1、获取文件流
FileInputStream fileInputStream=new FileInputStream(PATH + "OfficeAutomatic-System-master狂神观众统计表03.xls");
//2、创建一个工作簿,使用excel能操作的,这边斗可以操作
Workbook workbook = new HSSFWorkbook(fileInputStream);
//3、得到表
Sheet sheet=workbook.getSheetAt(0);//这里选择的是第零个表单
//得到行
Row row = sheet.getRow(0);
//得到列
Cell cell=row.getCell(0);
//获得数据中的字符串类型:getStringCellValue
//在读取值的时候一定要注意其中数据的类型
System.out.println(cell.getStringCellValue());
fileInputStream.close();
//2、创建一个工作表,表中的设置
//Sheet sheet=workbook.createSheet("狂神观众统计表");
}
获取不同值的类型:
其中重点为日期类型的转换。
public void testCellType() throws Exception{
//1、获取文件流
FileInputStream fileInputStream=new FileInputStream(PATH + "gz.xlsx");
//2、创建一个工作簿,使用excel能操作的,这边斗可以操作
Workbook workbook = new XSSFWorkbook(fileInputStream);
//选择第一个表来读
Sheet sheet = workbook.getSheetAt(0);
//获取标题内容
Row rowTitle = sheet.getRow(0);
if(rowTitle!=null){
//得到一行有多少数量,也就是多少列
int cellCount=rowTitle.getPhysicalNumberOfCells();
//读取第一行到每一列
for(int cellNum=0;cellNum<cellCount;cellNum++)
{
Cell cell=rowTitle.getCell(cellNum);
if(cell!=null){
//获取到每一列数据的类型
int cellType=cell.getCellType();
String cellvalue= cell.getStringCellValue();
System.out.print(cellvalue+ " | ");
}
}
System.out.print("\n");
}
//获取表中内容
//获取所有的行数
int rowCount = sheet.getPhysicalNumberOfRows();
//第0行为标题,所以这里为1
for (int rowNum = 1;rowNum<rowCount;rowNum++){
//循环得到每一行数据
Row rowData = sheet.getRow(rowNum);
if (rowData!=null){
//读取列
int cellCount = rowTitle.getPhysicalNumberOfCells();
for (int cellNum = 0;cellNum<cellCount;cellNum++){
System.out.print("["+(rowNum+1)+"-" +(cellNum+1+"]"));
Cell cell=rowData.getCell(cellNum);
//匹配列的数据类型
if(cell!=null){
int cellType=cell.getCellType();
String cellValue="";
switch (cellType){
case HSSFCell.CELL_TYPE_STRING://字符串
System.out.print("【String】");
cellValue=cell.getStringCellValue();
break;
case HSSFCell.CELL_TYPE_BOOLEAN://布尔
System.out.print("【BOOLEAN】");
cellValue=String.valueOf(cell.getBooleanCellValue());//进行布尔类型的转换
break;
case HSSFCell.CELL_TYPE_BLANK://空
System.out.print("【BLANK】");
break;
case HSSFCell.CELL_TYPE_NUMERIC://数字(日期和普通的数字)
System.out.print("【NUMERIC】");
if(HSSFDateUtil.isCellDateFormatted(cell)){
System.out.print("【日期】");
//取出日期类
Date date=cell.getDateCellValue();
cellValue=new DateTime(date).toString("yyyy-MM-dd");
}else{
//不是日期格式可以防止数字过长
System.out.print("【转换为字符串输出】");
cell.setCellType(HSSFCell.CELL_TYPE_STRING);
cellValue=cell.toString();
}
break;
case HSSFCell.CELL_TYPE_ERROR://布尔
System.out.print("【错误】");
break;
}
System.out.println(cellValue);
}
}
}
}
fileInputStream.close();
}
EasyExcel操作
相关的网址:https://www.yuque.com/easyexcel/doc/write,其中包括了读写相关的操作。
案例:
@Data
public class DemoData {
@ExcelProperty("字符串标题")
private String string;
@ExcelProperty("日期标题")
private Date date;
@ExcelProperty("数字标题")
private Double doubleData;
/**
* 忽略这个字段
*/
@ExcelIgnore
private String ignore;
}
/**
* 假设这个是你的DAO存储。当然还要这个类让spring管理,当然你不用需要存储,也不需要这个类。
**/
public class DemoDAO {
public void save(List<DemoData> list) {
//持久化操作
// 如果是mybatis,尽量别直接调用多次insert,自己写一个mapper里面新增一个方法batchInsert,所有数据一次性插入
}
}
// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
@Slf4j
public class DemoDataListener extends AnalysisEventListener<DemoData> {
/**
* 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收
*/
private static final int BATCH_COUNT = 5;
// private static final 1 JSON = ;
List<DemoData> list = new ArrayList<DemoData>();
/**
* 缓存的数据
*/
// 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
* 在读取数据的时候会使用
* DemoData 类型
* AnalysisContext 分析上下文
*/
@Override
public void invoke(DemoData data, AnalysisContext context) {
System.out.println(JSON.toJSONString(data));
// log.info("解析到一条数据:{}", JSON.toJSONString(data));
// cachedDataList.add(data);
list.add(data);
// 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
if (list.size() >= BATCH_COUNT) {
saveData();
// 存储完成清理 list
// cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
list.clear();
}
}
/**
* 所有数据解析完成了 都会来调用
*
* @param context
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 这里也要保存数据,确保最后遗留的数据也存储到数据库
saveData();
log.info("所有数据解析完成!");
}
/**
* 加上存储数据库
*/
private void saveData() {
log.info("{}条数据,开始存储数据库!", list.size());
demoDAO.save(list);
log.info("存储数据库成功!");
}
// @Override
// public void invoke(DemoData demoData, AnalysisContext analysisContext) {
//
// }
}
public class EasyTest {
String PATH ="E:\\path\\";
private List<DemoData> data() {
List<DemoData> list = new ArrayList<DemoData>();
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;
}
//根据list 写入到excel
@org.junit.Test
public void simpleWrite(){
// 注意 simpleWrite在数据量不大的情况下可以使用(5000以内,具体也要看实际情况),数据量大参照 重复多次写入
// 写法1 JDK8+
// since: 3.0.0-beta1
String fileName = PATH+"EasyTest.xlsx";
// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
// 如果这里想使用03 则 传入 excelType参数即可
//fileName是一个流,会自动生成xlsx文件
//DemoData.class其中包括的实体类
//sheet写入的表名 write (filename,格式类) doWrite()写入的数据
EasyExcel.write(fileName, DemoData.class).sheet("模板").doWrite(data());
// 写法2
// fileName = TestFileUtil.getPath() + "simpleWrite" + System.currentTimeMillis() + ".xlsx";
// // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
// // 如果这里想使用03 则 传入excelType参数即可
// EasyExcel.write(fileName, DemoData.class).sheet("模板").doWrite(data());
//
// // 写法3:使用 try-with-resources @since 3.1.0
}
@org.junit.Test
public void simpleRead(){
// 注意 simpleWrite在数据量不大的情况下可以使用(5000以内,具体也要看实际情况),数据量大参照 重复多次写入
// 写法1 JDK8+
// since: 3.0.0-beta1
String fileName = PATH+"EasyTest.xlsx";
// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
// 如果这里想使用03 则 传入 excelType参数即可
//fileName是一个流,会自动生成xlsx文件
//DemoData.class其中包括的实体类
//sheet写入的表名 write (filename,格式类) doWrite()写入的数据
// EasyExcel.write(fileName, DemoData.class).sheet("模板").doWrite(data());
EasyExcel.read(fileName,DemoData.class,new DemoDataListener()).sheet().doRead();
// 写法2
// fileName = TestFileUtil.getPath() + "simpleWrite" + System.currentTimeMillis() + ".xlsx";
// // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
// // 如果这里想使用03 则 传入excelType参数即可
// EasyExcel.write(fileName, DemoData.class).sheet("模板").doWrite(data());
//
// // 写法3:使用 try-with-resources @since 3.1.0
}
}
读取之后输出的数据:
EasyExcel.read(fileName,DemoData.class,new DemoDataListener()).sheet().doRead();
// 写法2
// fileName = TestFileUtil.getPath() + "simpleWrite" + System.currentTimeMillis() + ".xlsx";
// // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
// // 如果这里想使用03 则 传入excelType参数即可
// EasyExcel.write(fileName, DemoData.class).sheet("模板").doWrite(data());
//
// // 写法3:使用 try-with-resources @since 3.1.0
}
}