您的位置:首页 > 其它

使用SXSSFWorkbook导出大量excel表格

2017-11-17 21:18 274 查看
前言:这几天遇到一个需求,需要从数据库读取记录然后导出成excel表格,

在这个过程遇到很多问题,从最开始使用HSSFWorkbook到XSSFWorkbook,最后使用

SXSSFWorkbook。

先来了解下为什么用SXSSFWorkbook,在源码中发现属性名_randomAccessWindowSize,

指定内存中缓存的最大行数,他就是SXSSFWorkbook能导出大量数据关键因素, 具体怎

么实现的呢,一步步查看到SXSSFSheet源码,在创建行的方法中(加粗部分),当达到设

置的最大行数时就flushRows 操作,把当前内存中行数写到磁盘中去

public Row createRow(int rownum) {
int maxrow = SpreadsheetVersion.EXCEL2007.getLastRowIndex();
if (rownum >= 0 && rownum <= maxrow) {
Row previousRow = rownum > 0 ? this.getRow(rownum - 1) : null;
int initialAllocationSize = 0;
if (previousRow != null) {
initialAllocationSize = previousRow.getLastCellNum();
}

if (initialAllocationSize <= 0 && this._writer.getNumberOfFlushedRows() > 0) {
initialAllocationSize = this._writer.getNumberOfCellsOfLastFlushedRow();
}

if (initialAllocationSize <= 0) {
initialAllocationSize = 10;
}

SXSSFRow newRow = new SXSSFRow(this, initialAllocationSize);
this._rows.put(new Integer(rownum), newRow);
  if (this._randomAccessWindowSize >= 0 && this._rows.size() > this._randomAccessWindowSize) {
try {
this.flushRows(this._randomAccessWindowSize);
} catch (IOException var7) {
throw new RuntimeException(var7);
}
}

return newRow;
} else {
throw new IllegalArgumentException("Invalid row number (" + rownum + ") outside allowable range (0.." + maxrow + ")");
}
}

SXSSFWorkbook已经帮我们解决了一大问题了,接下来又遇到问题,我这次导出数据量在

10万行左右,列有15列,一次性查询所有数据的话会造成内存突然大量占用,某些情况可能

会造成服务奔溃,继续分段查询数据

 伪代码

public int exportExcel(){

XSSFWorkbook xssfWb = null;
SXSSFWorkbook sxssfWorkbook = null;
SXSSFSheet sxssSheet = null;
FileOutputStream fos = null;
try {
//内存缓存最大行数
int rowMaxCache = 100;

xssfWb = new XSSFWorkbook();
//rowMaxCache(可选)不声明默认100
sxssfWorkbook = new SXSSFWorkbook(xssfWb, rowMaxCache);
sxssSheet = (SXSSFSheet) sxssfWorkbook.createSheet("sheet标题名");
//记录总数
int listNum = 查询数据库;
//查询数据库最大记录数
int sourceMaxCache = 10000;
//总共查询次数
int findNum = 0;
if (0 == (listNum % sourceMaxCache)){
findNum = listNum / sourceMaxCache;
}else {
findNum = listNum / sourceMaxCache + 1;
}

for (int j = 0; j < findNum; j++){

//mapParam作为查询条件限制,这里只设置设置数据库中的limit
mapParam.put("start",j * sourceMaxCache + 1);
mapParam.put("end",sourceMaxCache);

//导出记录
List<Map<String,Object>> list = 查询数据库操作(mapParam);
//行数
int exportNum = list.size();
//列数
int columnNum = list.get(0).size();

// 首行 标题
if (0 == j){
Row row0 = sxssSheet.createRow(0);
Cell sxssC1 = row0.createCell(0);
sxssC1.setCellValue("标题");
//设置单元格样式 setStyle
sxssC1.setCellStyle(setStyle(sxssfWorkbook));
//合并  参数1:起始行号 参数2:终止行号 参数3:起始列号 参数4:终止列号
sxssSheet.addMergedRegion(new CellRangeAddress(0, 0, 0, exportNum));
}

//第二行 表头
if (0 == j){
Row row1 = sxssSheet.createRow(1);
for (int k = 0; k < columnNum; k++){
// Row row, int column
4000
, String value, CellStyle style
// 行 ,列号,单元格值,样式(可选)
CellUtil.createCell(row1, 0, value, setStyle(sxssfWorkbook));
}
}

//数据
for (int i = 0; i < exportNum; i++) {
Row row = sxssSheet.createRow(j * sourceMaxCache + i + 2);
Map<String, Object> map = list.get(i);
// 数据
for (int k = 0; k < columnNum; k++){
// Row row, int column, String value, CellStyle style
// 行 ,列号,单元格值
CellUtil.createCell(row1, 0, value);
}
}
//帮助gc回收内存
list.clear();
}
//创建文件导出文件路径
File f = new File(currStr);
try {
if (!f.exists()) {
f.createNewFile();
}
} catch (IOException e1) {
return "0";
}
fos = new FileOutputStream(f);
sxssfWorkbook.write(fos);

} catch (Exception e) {
e.printStackTrace();
return "0";
}finally{
try {
if(fos != null){
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}


maven 引入包

<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.9</version>
</dependency>

<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.9</version>
</dependency>

<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml-schemas</artifactId>
<version>3.9</version>
</dependency>

总结,代码虽然能实现大量数据的excel表格导出,但还有优化问题,

1、时间和空间平衡;为了解决上面内存占用问题,我采用了分批次查询数据库,来减小服务

器压力,但这样有引出另个问题,因为多次查询数据库,造成每次数据写完excel后会有等待

数据返回时间,而且对sql查询性能优化显得也更为重要。

2、样式问题:读者可能会奇怪我除了对表头和标题加入样式,正式数据却没有加入样式,

因为在我测试过程中,对每个单元加入样式等其他操作时,会严重影响性能,在一番网上查询

资料未解决后才得以放弃,因为楼主觉得为了样式而牺牲性能有些不值得(毕竟为每个单元格

设置样式可是行列相乘)

希望有读者有更好的方案提出来一起交流,如果发现代码逻辑

有什么错误也请及时指出
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: