1.先看一下普通写法
public class ExcelExport {
public static void main(String[] args) {
// 输出文件路径
String filePath = "D:/demo.xlsx";
// Sheet名称
String sheetName = "Sheet1";
// 写入数据的Sheet编号
int writeSheet = 0;
// 写入数据的Table编号
int writeTable = 0;
// 创建ExcelWriter对象
ExcelWriter excelWriter = EasyExcel.write(filePath, DemoData.class).sheet(sheetName).build();
// 准备数据源
List<DemoData> data = new ArrayList<>();
for (int i = 0; i < 1000000; i++) {
DemoData demoData = new DemoData();
demoData.setId(i);
demoData.setName("name" + i);
data.add(demoData);
}
// 将数据写入Excel文件中
excelWriter.write0(data, writeSheet, writeTable);
// 关闭ExcelWriter对象,释放资源
excelWriter.finish();
}
}
需要注意的是,在进行大数据量导出时,为了避免内存溢出,可以考虑使用分页查询或按批次查询的方式读取数据,并在写入Excel文件时采用流式写入或多线程写入的方式,这样可以提高导出效率,并避免内存溢出的问题
2.避免内存溢出的问题我们采取分页
public class ExcelStreamWrite {
public static void main(String[] args) {
// 输出文件路径
String filePath = "D:/demo.xlsx";
// Sheet名称
String sheetName = "Sheet1";
// 写入数据的Sheet编号
int writeSheet = 0;
// 创建ExcelWriter对象
ExcelWriter excelWriter = EasyExcel.write(filePath, DemoData.class).sheet(sheetName).build();
// 准备数据源
List<DemoData> data = new ArrayList<>();
for (int i = 0; i < 1000000; i++) {
DemoData demoData = new DemoData();
demoData.setId(i);
demoData.setName("name" + i);
data.add(demoData);
}
// 定义每次写入的数据量
int batchSize = 1000;
// 定义写入的起始位置
int start = 0;
// 定义写入的结束位置
int end = 0;
// 循环写入数据
while (end < data.size()) {
// 计算下一次写入的结束位置
end = Math.min(start + batchSize, data.size());
// 获取本次写入的数据
List<DemoData> subList = data.subList(start, end);
// 写入数据
excelWriter.write(subList, EasyExcel.writerSheet(writeSheet).build());
// 更新起始位置
start = end;
}
// 关闭ExcelWriter对象,释放资源
excelWriter.finish();
}
}
3.为了提高效率我们多线程导出
- 定义一个数据读取器,用于从数据源中读取指定页码的数据。
public interface DataReader<T> {
/**
* 读取指定页码的数据
*
* @param pageNum 页码,从1开始
* @param pageSize 每页数据量
* @return 当前页的数据
*/
List<T> readData(int pageNum, int pageSize);
}
- 定义一个数据写入器,用于将指定页码的数据写入Excel中。
public interface DataWriter<T> {
/**
* 写入指定页码的数据
*
* @param pageNum 页码,从1开始
* @param data 数据
*/
void writeData(int pageNum, List<T> data);
}
- 定义一个导出器,用于启动多线程进行数据导出。
public class ExcelExport<T> {
private final DataReader<T> reader;
private final DataWriter<T> writer;
public ExcelExport(DataReader<T> reader, DataWriter<T> writer) {
this.reader = reader;
this.writer = writer;
}
public void export(int threadNum, int pageSize) {
ExecutorService executorService = Executors.newFixedThreadPool(threadNum);
CountDownLatch countDownLatch = new CountDownLatch(threadNum);
for (int i = 0; i < threadNum; i++) {
final int threadIndex = i;
executorService.execute(() -> {
int pageNum = threadIndex + 1;
List<T> data = reader.readData(pageNum, pageSize);
while (data != null && !data.isEmpty()) {
writer.writeData(pageNum, data);
pageNum += threadNum;
data = reader.readData(pageNum, pageSize);
}
countDownLatch.countDown();
});
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
executorService.shutdown();
}
}
}
在上面的代码中,我们启动了一个固定线程数的线程池,并使用CountDownLatch实现线程同步。对于每个线程,我们从指定的数据读取器中读取指定页码的数据,然后写入到指定的数据写入器中,直到读取到的数据为空或空集合为止。需要注意的是,每个线程的页码应该从不同的起始位置开始,以保证每个线程读取的数据不重复,也不遗漏。
使用示例代码如下:
public class ExcelExportDemo {
public static void main(String[] args) {
// 数据读取器
DataReader<DemoData> reader = new DemoDataReader();
// 数据写入器
DataWriter<DemoData> writer = new DemoDataWriter();
// 导出器
ExcelExport<DemoData> export = new ExcelExport<>(reader, writer);
// 启动多线程分页导出
export.export(4, 10000);
}
}
测试用例代码
public class ExcelExportDemo {
public static void main(String[] args) {
// 输出文件路径
String filePath = "D:/demo.xlsx";
// Sheet名称
String sheetName = "Sheet1";
// 写入数据的Sheet编号
int writeSheet = 0;
// 创建ExcelWriter对象
ExcelWriter excelWriter = EasyExcel.write(filePath, DemoData.class).sheet(sheetName).build();
// 定义每页数据量
int pageSize = 10000;
// 定义数据总量
int totalCount = 100000;
// 定义分页查询器
PageQuery<DemoData> pageQuery = new PageQuery<>(pageNum -> {
// 模拟从数据库中查询指定页码的数据
List<DemoData> data = new ArrayList<>();
int startIndex = (pageNum - 1) * pageSize;
int endIndex = Math.min(startIndex + pageSize, totalCount);
for (int i = startIndex; i < endIndex; i++) {
DemoData demoData = new DemoData();
demoData.setId(i);
demoData.setName("name" + i);
data.add(demoData);
}
return data;
});
// 定义数据写入器
DataWriter<DemoData> dataWriter = new ExcelDataWriter<>(excelWriter, writeSheet);
// 定义导出器
ExcelExport<DemoData> export = new ExcelExport<>(pageQuery, dataWriter);
// 启动多线程分页导出
export.export(4);
// 关闭ExcelWriter对象,释放资源
excelWriter.finish();
}
}
public class ExcelDataWriter<T> implements DataWriter<T> {
private final ExcelWriter excelWriter;
private final int writeSheet;
public ExcelDataWriter(ExcelWriter excelWriter, int writeSheet) {
this.excelWriter = excelWriter;
this.writeSheet = writeSheet;
}
@Override
public void writeData(int pageNum, List<T> data) {
WriteSheet writeSheet = EasyExcel.writerSheet(this.writeSheet).build();
excelWriter.write(data, writeSheet);
}
}
在上面的示例代码中,我们使用了一个PageQuery和一个ExcelDataWriter分别实现了DataReader和DataWriter接口,用于分页查询和写入数据。每个线程读取10000条数据,共启动4个线程进行数据导出。使用EasyExcel进行流式写入,避免一次性将全部数据加载到内存中,从而避免内存溢出的问题。在测试环境下,可以在几秒钟内导出100000条数据。实际的导出效率取决于具体的硬件环境和数据源的复杂度。