使用SXSSFWorkbook导出大量excel表格

前言:这几天遇到一个需求,需要从数据库读取记录然后导出成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> 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, 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 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 引入包


	org.apache.poi
	poi
	3.9



	org.apache.poi
	poi-ooxml
	3.9



	org.apache.poi
	poi-ooxml-schemas
	3.9

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

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

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

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

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

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

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

设置样式可是行列相乘)


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

有什么错误也请及时指出


你可能感兴趣的:(excel导出)