一、准备
对于大量数据输出,采用ExcelWriter容易引起内存溢出,因此有了BigExcelWriter。
* ExcelUtil Excel工具类,读取的快捷方法都被封装于此
* ExcelReader Excel读取器,Excel读取的封装,可以直接构造后使用。
* ExcelWriter Excel生成并写出器,Excel写出的封装(写出到流或者文件),可以直接构造后使用。
cn.hutool
hutool-all
5.0.7
org.apache.poi
poi-ooxml
4.1.2
xerces
xercesImpl
2.12.0
二、代码示例
package com.yl.excel;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.json.JSON;
import cn.hutool.poi.excel.BigExcelWriter;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import cn.hutool.poi.excel.sax.Excel03SaxReader;
import cn.hutool.poi.excel.sax.Excel07SaxReader;
import cn.hutool.poi.excel.sax.handler.RowHandler;
import cn.hutool.poi.word.Word07Writer;
import com.yl.entity.User;
import lombok.extern.slf4j.Slf4j;
import java.awt.*;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;
/**
* 描述: Excel大数据生成-BigExcelWriter
* 对于大量数据输出,采用ExcelWriter容易引起内存溢出,因此有了BigExcelWriter,使用方法与ExcelWriter完全一致
*
* @author: yanglin
* @Date: 2020-07-13-16:19
* @Version: 1.0
*/
@Slf4j
public class HutoolExcel {
/**
* ExcelUtil Excel工具类,读取的快捷方法都被封装于此
* ExcelReader Excel读取器,Excel读取的封装,可以直接构造后使用。
* ExcelWriter Excel生成并写出器,Excel写出的封装(写出到流或者文件),可以直接构造后使用。
*/
/**
* 创建Excel
* @param excelName
*/
public static void testCreateExcel(String excelName){
List> row1 = CollUtil.newArrayList("aa", "bb", "cc", "dd", DateUtil.date(), 3.22676575765);
List> row2 = CollUtil.newArrayList("aa1", "bb1", "cc1", "dd1", DateUtil.date(), 250.7676);
List> row3 = CollUtil.newArrayList("aa2", "bb2", "cc2", "dd2", DateUtil.date(), 0.111);
List> row4 = CollUtil.newArrayList("aa3", "bb3", "cc3", "dd3", DateUtil.date(), 35);
List> row5 = CollUtil.newArrayList("aa4", "bb4", "cc4", "dd4", DateUtil.date(), 28.00);
List> rows = CollUtil.newArrayList(row1, row2, row3, row4, row5);
BigExcelWriter writer= ExcelUtil.getBigWriter(excelName);
// 一次性写出内容,使用默认样式
writer.write(rows);
// 关闭writer,释放内存
writer.close();
}
/**
* 创建Word
* @param wordName
*/
public static void testCreateWord(String wordName){
Word07Writer writer = new Word07Writer();
// 添加段落(标题)
writer.addText(new Font("方正小标宋简体", Font.PLAIN, 22), "我是第一部分", "我是第二部分");
// 添加段落(正文)
writer.addText(new Font("宋体", Font.PLAIN, 22), "我是正文第一部分", "我是正文第二部分");
// 写出到文件
writer.flush(FileUtil.file(wordName));
// 关闭
writer.close();
}
/**
* Excel读取-ExcelReader
* @param excelName
*/
public static void readExcel(String excelName){
// 读取Excel中所有行和列,都用列表表示
ExcelReader reader = ExcelUtil.getReader(excelName);
List> readAll = reader.read();
// 读取为Map列表,默认第一行为标题行,Map中的key为标题,value为标题对应的单元格值。
ExcelReader readerMap = ExcelUtil.getReader(excelName);
List> readMapAll = readerMap.readAll();
// 读取为Bean列表,Bean中的字段名为标题,字段值为标题对应的单元格值。
ExcelReader readerClass = ExcelUtil.getReader("d:/aaa.xlsx");
List all = readerClass.readAll(Object.class);
}
/**
* 流方式读取Excel2003-Excel03SaxReader
* Excel03SaxReader只支持Excel2003格式的Sax读取。
* 在标准的ExcelReader中,如果数据量较大,读取Excel会非常缓慢,并有可能造成内存溢出。因此针对大数据量的Excel,Hutool封装了event模式的读取方式。
* @param excelName
*/
public static void readStream2003Excel(String excelName){
// ExcelUtil快速读取
ExcelUtil.read03BySax(excelName, 1, createRowHandler());
// 构建对象读取
Excel03SaxReader reader = new Excel03SaxReader(createRowHandler());
// reader方法的第二个参数是sheet的序号,-1表示读取所有sheet,0表示第一个sheet,依此类推。
reader.read(excelName, 0);
}
/**
* 首先我们实现一下RowHandler接口,这个接口是Sax读取的核心,通过实现handle方法编写我们要对每行数据的操作方式
* (比如按照行入库,入List或者写出到文件等)
* @return
*/
private static RowHandler createRowHandler() {
return new RowHandler() {
@Override
public void handle(int sheetIndex, int rowIndex, List rowlist) {
log.info("[{}] [{}] {}", sheetIndex, rowIndex, rowlist);
}
};
}
/**
* 流方式读取Excel2007-Excel07SaxReader
* 在标准的ExcelReader中,如果数据量较大,读取Excel会非常缓慢,并有可能造成内存溢出。因此针对大数据量的Excel,Hutool封装了Sax模式的读取方式。
* Excel07SaxReader只支持Excel2007格式的Sax读取。
* @param excelName
*/
public static void readStream2007Exce(String excelName){
// ExcelUtil快速读取
ExcelUtil.read07BySax(excelName, 0, createRowHandler());
// 构建对象读取
Excel07SaxReader reader = new Excel07SaxReader(createRowHandler());
reader.read(excelName, 0);
}
/**
* Excel生成-ExcelWriter
* Hutool将Excel写出封装为ExcelWriter,原理为包装了Workbook对象,每次调用merge(合并单元格)或者
* write(写出数据)方法后只是将数据写入到Workbook,并不写出文件,只有调用flush或者close方法后才会真正写出文件。
* 由于机制原因,在写出结束后需要关闭ExcelWriter对象,调用close方法即可关闭,此时才会释放Workbook对象资源,
* 否则带有数据的Workbook一直会常驻内存。
*
* @param toExcelName
*/
public static void createExcel(String toExcelName){
// 1. 将行列对象写出到Excel
List row1 = CollUtil.newArrayList("aa", "bb", "cc", "dd");
List row2 = CollUtil.newArrayList("aa1", "bb1", "cc1", "dd1");
List row3 = CollUtil.newArrayList("aa2", "bb2", "cc2", "dd2");
List row4 = CollUtil.newArrayList("aa3", "bb3", "cc3", "dd3");
List row5 = CollUtil.newArrayList("aa4", "bb4", "cc4", "dd4");
List> rows = CollUtil.newArrayList(row1, row2, row3, row4, row5);
//通过工具类创建writer
ExcelWriter writer = ExcelUtil.getWriter(toExcelName);
//通过构造方法创建writer
//ExcelWriter writer = new ExcelWriter("d:/writeTest.xls");
//跳过当前行,既第一行,非必须,在此演示用
writer.passCurrentRow();
//合并单元格后的标题行,使用默认标题样式
writer.merge(row1.size() - 1, "测试标题");
//一次性写出内容,强制输出标题
writer.write(rows, true);
//关闭writer,释放内存
writer.close();
}
/**
* 测试excel导出百万条记录
* @param data
* @param toExcelName
*/
public static void createExcelData(List> data, String toExcelName){
//通过工具类创建writer
BigExcelWriter writer= ExcelUtil.getBigWriter(toExcelName);
// 一次性写出内容,使用默认样式
writer.write(data);
// 关闭writer,释放内存
writer.close();
}
public static List> createMillionExcelData(){
// 测试excel导出百万条记录 创建测试数据
User user;
List> users = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
user = User.builder().id(i).name("东芝王哥"+i).sex("Y").age(10+i)
.love("吃饭睡觉大殴打"+i).eat("吃大虾"+i).run("徒步5000"+i+"米")
.idCard("42595956874412454554"+i).birthday(LocalDateTime.now())
.securityCode("1245"+i).account("adminqwe"+i).password("qwer"+i)
.remark("z这是一个爱好学习、天天向上的bgm"+i)
.build();
// log.info("第 {} 个user::{}", i, user.toString());
String[] userStr = user.toString().substring(
user.toString().indexOf("(") + 1, user.toString().lastIndexOf(")")).split(",");
List userStrs = CollUtil.newArrayList(userStr);
List userStrNews = new ArrayList<>();
if (i == 0) {
userStrs.forEach( u -> {
u = u.split("=")[0];
userStrNews.add(u);
});
}else{
userStrs.forEach( u -> {
u = u.split("=")[1];
userStrNews.add(u);
});
}
users.add(userStrNews);
}
return users;
}
/**
* 并发创建测试数据
*
* @param toExcelName
* @throws ExecutionException
* @throws InterruptedException
*/
public static void createMillionExcelDataFuture(String toExcelName) throws ExecutionException, InterruptedException {
long start = System.currentTimeMillis();
log.info("createMillionExcelDataFuture start {} ", start);
// 定时多个FutureTask生成数据
FutureTask>> taskOne = new FutureTask(new Callable() {
@Override
public Object call() throws Exception {
return createMillionExcelData();
}
});
FutureTask>> taskTwo = new FutureTask(new Callable() {
@Override
public Object call() throws Exception {
return createMillionExcelData();
}
});
Thread thread1 = new Thread(taskOne);
thread1.start();
Thread thread2 = new Thread(taskTwo);
thread2.start();
List> data = new ArrayList<>();
// 得到FutureTask生成的结果 阻塞
List> oneList = taskOne.get();
data.addAll(oneList);
List> twoList = taskTwo.get();
data.addAll(twoList);
// 设置表头
// 生成excel
//通过工具类创建writer
BigExcelWriter writer= ExcelUtil.getBigWriter(toExcelName);
// 一次性写出内容,使用默认样式
writer.write(data);
// 关闭writer,释放内存
writer.close();
log.info("createMillionExcelDataFuture end 耗时 {}", System.currentTimeMillis() - start);
}
public static void main(String[] args) {
String proDir = System.getProperty("user.dir") +"/springboot-jacob/src/main/resources";
String excelName = proDir + "/excel/"+System.currentTimeMillis()+"test.xlsx";
/*String wordName = proDir + "/word/"+System.currentTimeMillis()+"test.doc";
log.info("创建一个excel.................");
testCreateExcel(excelName);
log.info("创建一个word.................");
testCreateWord(excelName);*/
try {
createMillionExcelDataFuture(excelName);
} catch (ExecutionException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
参考Hutool文档 https://www.hutool.cn/docs/#/poi/Excel%E5%A4%A7%E6%95%B0%E6%8D%AE%E7%94%9F%E6%88%90-BigExcelWriter
以上