总结:目前市面上流行的2种 EasyExcel和POI都不是真正的对物理excel文件进行追加导入。
只是在缓存里面追加,最后一次性写入,并不能解决内存占用问题。
无非就是下面两种逻辑:
1.for循环查询数据,将数据写入缓存,最后一次性写入excel。
2.将已有的excel通过FIleInputStream流读出来,加载到内存当中,然后获取对应sheet页的行数,进行追加操作。
PS:建议使用CSV文件格式,直接使用Java原生的FileUtils追加写入
FileUtils.writeStringToFile(file, sb.toString(), "GBK", true);
下面来讲讲easyExcel、POI、csv相关的代码写法:
要求:
1.对表头进行排序过滤
2.对列进行排序过滤
官网地址:EasyExcel官网
我们使用新版本的,旧的对于字段排序/过滤都不支持
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.3.2</version>
</dependency>
//此为线程池异步导出
commonExecutor.execute(() -> {
File file = null;
ExcelWriter excelWriter = null;
try {
file = File.createTempFile("a", ".xlsx");
file.deleteOnExit();
excelWriter = EasyExcel.write(file, UserVO.class)
.head(headlist) // 表头,传入一个list集合
.includeColumnFieldNames(showColumnList) // 对于UserVO要展示的列集合
.orderByIncludeColumn(true) // 是否根据showColumnList集合的顺序排序
.autoCloseStream(true) // 自动关闭流
.build();
WriteSheet writeSheet = EasyExcel.writerSheet("Sheet1").build();
// 循环 200/页查询数据
int pageNum = 1;
int pageSize = 200;
while (true) {
List<UserVO> voList = getData(pageNum, pageSize); // getData根据业务实现
// 将数据写入临时文件 此处循环追加就是写到内存里面
excelWriter.write(voList , writeSheet);
if (pageNum * pageSize >= count) {
break;
}
pageNum++;
}
// 数据处理完毕后,刷盘写入物理文件
excelWriter.finish();
} catch (Exception e) {
log.error("导出 异常:" + e.getMessage(), e);
} finally {
// 删除临时文件
if (!Objects.isNull(file)) {
file.delete();
}
}
});
唉~别急,我再写个POI的。
网传SXSSFWorkbook可以实现追加写入,然后实操后,其实是覆盖写入。
//SXSSFWorkbook 和 XSSFWorkbook 都差不多,只是定义不同,写法都差不多
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
public class ExcelAppender {
public static void main(String[] args) {
String filePath = "C:\\Local\\Temp\\11.xlsx";
// 构造一个查询数据的列表
List<String> queryDataList = List.of("query4", "query5", "query6");
// 加载已存在的 Excel 文件
try (Workbook workbook = new SXSSFWorkbook(new FileInputStream(new File(filePath)))) {
Sheet sheet = workbook.getSheetAt(0);
// 获取已存在数据的最后一行索引
int lastRowNum = sheet.getLastRowNum();
// 在最后一行索引的下一行开始追加写入查询数据
int rowNum = lastRowNum + 1;
for (String queryData : queryDataList) {
Row row = sheet.createRow(rowNum++);
Cell cell = row.createCell(0);
cell.setCellValue(queryData);
}
// 保存修改后的工作簿到文件
try (FileOutputStream outputStream = new FileOutputStream(new File(filePath))) {
workbook.write(outputStream);
System.out.println("查询数据已覆盖写入到 Excel 文件:" + filePath);
} catch (IOException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
最后,dangdangdang~
来写我最终选择的写法CSV,就是追加物理文件数据。很不错。
commonExecutor.execute(() -> {
File temp = null;
try {
temp = File.createTempFile("aaa", ".csv");
temp.deleteOnExit();
List<List<String>> fillData = new ArrayList<>();
// 1.初始化表头,showNameList是表头集合,有顺序的哈~
fillData.add(ImmutableList.of(showNameList.toString()));
// 2.循环 200/页查询数据
int pageNum = 1;
int pageSize = 200;
while (true) {
List<UserVO> recordVOList = getData(pageNum, pageSize);
// 3.填充业务数据 我用的反射
fillData.addAll(ExcelUtil.convertSortEntityListToDataList(showColumnList, recordVOList));
StringBuilder sb = new StringBuilder();
for (List<String> rowData : fillData) {
sb.append(String.join(",", rowData));
sb.append(System.lineSeparator());
}
try {
FileUtils.writeStringToFile(temp, outPutStr, "GBK", true);
} catch (IOException e) {
log.error("写入CSV 异常:{}", e.getMessage(), e);
return;
}
if (pageNum * pageSize >= count) {
break;
}
pageNum++;
fillData.clear();
}
} catch (Exception e) {
log.error("导出 异常:{}", e.getMessage(), e);
} finally {
// 4.删除临时文件
if (!Objects.isNull(temp)) {
temp.delete();
}
}
});