1.SXSSFWorkbook理解:
  SXSSFWorkbook是用来生成海量excel数据文件,主要原理是借助临时存储空间生成excel,SXSSFWorkbook专门处理大数据,对于大型excel的创建且不会内存溢出的,就只有SXSSFWorkbook了。它的原理很简单,用硬盘空间换内存(就像hashmap用空间换时间一样)。 SXSSFWorkbook是streaming版本的XSSFWorkbook,它只会保存最新的excel rows在内存里供查看,在此之前的excel rows都会被写入到硬盘里(Windows电脑的话,是写入到C盘根目录下的temp文件夹)。被写入到硬盘里的rows是不可见的/不可访问的。只有还保存在内存里的才可以被访问到。
注:HSSFWorkbook和XSSFWorkbook的Excel Sheet导出条数上限(<=2003版)是65535行、256列,(>=2007版)是1048576行,16384列,如果数据量超过了此上限,那么可以使用SXSSFWorkbook来导出。实际上上万条数据,甚至上千条数据就可以考虑使用SXSSFWorkbook了。
注意:首先需要引入依赖:注意:4.0.0版本的JDK需要1.8以上,如果JDK是1.7的,那么就使用3.9版本的依赖

2.数据过多使用SXSSFWorkbook也是会出现内存溢出的问题,主要出现的两个地方:
a.从数据库读取数据到内存时溢出。
    优化角度1:取数据时用分页的方法分批取数据,然后写入sheet中。这样就可以避免取数据时内存溢出;
    java.lang.OutOfMemoryError:GC overhead limit exceeded
b.FileOutputStream os = new FileOutputStream(path); wb.write(os);
    优化角度2:    创建Workbook时设置工作簿保存在内存中数据的条数,这样一旦这个Workbook中数据量超过1000就会写入到磁盘中,减少内存的使用量来提高速度和避免溢出。
    Workbook wb = new SXSSFWorkbook(1000);

3.通过分页查询实现海量excle数据的导出,避免发生OOM。代码如下:

@RequestMapping("/exportPurchaseInfo.do")
@ResponseBody
public void exportPurchaseGoodsInfo(HttpServletRequest request, HttpServletResponse response,QueryParamDto reqDto) {
try {
int pageSize = 1000;//分页的大小,即每次分页查询多少条记录开关
String[] headers = {"序号","商品编号","商品名称","品类","品牌","采购金额(元)","采购数量(件)"};
SXSSFWorkbook workbook = ExportExcelByPageUtil.makeSXSSFWorkbook(purchseReportExportExcelByPageService,reqDto, headers, pageSize);
ExportExcelByPageUtil.exportExcel(request, response, workbook, "商品销售报表.xlsx"); } catch (Exception e) {
logger.error("导出商品销售报表异常",e);
} }

ExportExcelByPageUtil.java

public static <T> SXSSFWorkbook makeSXSSFWorkbook(ReportExportExcelByPageService<T> exportExcelByPageTyService, T queryDataBo, String[] headers, int pageSize){
int rowAccessWindowSize = 100;
//这样表示SXSSFWorkbook只会保留100条数据在内存中,其它的数据都会写到磁盘里,这样的话占用的内存就会很少
SXSSFWorkbook workbook = new SXSSFWorkbook(rowAccessWindowSize);
SXSSFSheet sheet = (SXSSFSheet) workbook.createSheet("sheet1");
Map<String,CellStyle> cellStyles=getCellStyle(workbook);
CellStyle cs = cellStyles.get("cs");
CellStyle cs2 = cellStyles.get("cs2"); //设置列名
Row row = sheet.createRow((short) 0);
for (int i = 0; i < headers.length; i++) {
Cell cell = row.createCell(i);
cell.setCellValue(headers[i]);
cell.setCellStyle(cs);
} String pattern = "yyyy-MM-dd HH:mm:ss";
String beginTime = DateUtil.formatDate(new Date(), pattern);
int allCount = exportExcelByPageTyService.queryAllCount(queryDataBo);
if(allCount <= 0){
return workbook;
} //处理数据量超过一个sheet页最大量时异常,支持的最大导出数据量为10000
int maxExportCount = 10000;
allCount = allCount > maxExportCount ? maxExportCount : allCount;
//page:查询总数据量为 allCount条记录时,每次生成pageSize条记录 需要page 次进行导出
int page = (int) Math.ceil((double) allCount / (double) pageSize);
//101条记录 aliquotFlag=false
boolean aliquotFlag = allCount % pageSize == 0; SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
for(int i = 0; i < page; i++){
logger.error("第"+(i+1)+"次循环开始:"+ DateUtil.formatDate(new Date(), pattern));
int startIndex = i*pageSize;
int maxCount = pageSize;
//最后一次的最大记录数
if(!aliquotFlag && i==(page-1)){
maxCount = allCount - startIndex;
}
logger.error("导出分页:startIndex==>"+startIndex+", pageSize==>"+maxCount);
List<String> ids = exportExcelByPageTyService.queryIdsInfo(queryDataBo, startIndex, maxCount);
List<LinkedHashMap<String, Object>> dataList = exportExcelByPageTyService.queryDatas(ids);
if(CollectionUtils.isNotEmpty(dataList)){
Set<Map.Entry<String, Object>> set = null;
Iterator<Map.Entry<String, Object>> iterator = null;
int j = 0;
for (int rowNum = 1, len = dataList.size() + 1; rowNum < len; rowNum++) {
Row row1 = sheet.createRow(startIndex+rowNum);
set = dataList.get(rowNum - 1).entrySet();
iterator = set.iterator();
j = 0;
while (iterator.hasNext()) {
Map.Entry<String, Object> entry = (Map.Entry<String, Object>) iterator.next();
String cellValue = "";
if(entry.getValue()==null){
cellValue = "";
}else if(entry.getValue() instanceof Timestamp){
cellValue = sdf.format((Timestamp) entry.getValue());
} else {
cellValue = String.valueOf(entry.getValue());
}
Cell cell = row1.createCell(j);
cell.setCellValue(cellValue);
cell.setCellStyle(cs2);
j++;
}
if (rowNum % rowAccessWindowSize == 0) {
try {
sheet.flushRows();
} catch (IOException e) {
logger.error("sheet从内存写入本地硬盘失败", e);
}
}
}
}
dataList = null;
logger.error("第"+(i+1)+"次循环结束:"+formatDate(new Date(), pattern));
}
logger.error("导出分页开始:"+beginTime);
logger.error("导出分页结束:"+ formatDate(new Date(), pattern));
return workbook;
}
public static boolean exportExcel(HttpServletRequest request, HttpServletResponse response,SXSSFWorkbook workbook, String fileName){
OutputStream out = null;
try {
String userAgent = request.getHeader("USER-AGENT");
fileName = dealChineseFileName(userAgent, fileName);
out = response.getOutputStream();
response.setContentType("application/x-download");
response.setHeader("Pragma", "public");
response.setHeader("Cache-Control", "max-age=30");
response.setHeader("Content-disposition", "attachment;filename=" + fileName);
workbook.write(out);
out.flush();
} catch (Exception e) {
logger.error("导出Excel文件失败", e);
return false;
}finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
logger.error("导出Excel文件关闭输出流失败", e);
} finally {
out = null;
}
}
if(workbook!=null){
try {
workbook.dispose();
workbook.close();
} catch (IOException e) {
logger.error("导出Excel文件关闭输出流失败", e);
} finally {
workbook = null;
}
}
}
return true;
}

ReportExportExcelByPageService.java

public interface ReportExportExcelByPageService<T> {
/** 查满足条件记录数 */
@Method(description = "查满足条件count")
public int queryAllCount(T queryDataBo);
/** 分页查满足条件Ids */
@Method(idempotent = true, retryTimes = 3, timeout = 30000, description = "分页查满足条件Ids")
public List<String> queryIdsInfo(T queryDataBo, int startIndex, int pageSize);
/** 根据Ids查数据 */
@Method(idempotent = true, retryTimes = 3, timeout = 30000, description = "根据Ids查数据")
public List<LinkedHashMap<String, Object>> queryDatas(List<String> ids);
}

ReportExportExcelByPageServiceImpl.java

public class ReportExportExcelByPageServiceImpl implements ReportExportExcelByPageService<QueryParamDto> {
protected Logger logger = LoggerFactory.getLogger(this.getClass()); @Override
public int queryAllCount(QueryParamDto queryDataBo) {
//查询记录数业务代码
} @Override
public List<String> queryIdsInfo(QueryParamDto queryDataBo, int startIndex, int pageSize) {
List<String> list = new ArrayList<String>();
list.add(queryDataBo.getBeginTime());
list.add(queryDataBo.getEndTime());
list.add(queryDataBo.getUserCode());
list.add(queryDataBo.getType());
list.add(queryDataBo.getAllType());
list.add(String.valueOf(startIndex));
list.add(String.valueOf(pageSize));
return list;
} @Override
public List<LinkedHashMap<String, Object>> queryDatas(final List<String> ids) {
List<LinkedHashMap<String, Object>> result = new ArrayList<>();
if (CollectionUtils.isEmpty(ids)) {
return result;
}
QueryParamDto queryDataBo=new QueryParamDto();
queryDataBo.setBeginTime( ids.get(0));
queryDataBo.setEndTime(ids.get(1));
queryDataBo.setUserCode(ids.get(2));
queryDataBo.setType(ids.get(3));
queryDataBo.setAllType(ids.get(4));
queryDataBo.setStartIndex(Integer.parseInt(ids.get(5)));
queryDataBo.setPageSize(Integer.parseInt(ids.get(6))); //根据分页的入参进行分页查询:返回结果为list
List<QueryResultData> commodityListInfo = ...;
/*
SELECT
zp.commodity_code commodityCode,
zp.commodity_name commodityName,
zp.category_name categoryName,
zp.brand brand,
sum(zp.purchase_money) purchaseMoney,
sum(zp.purchase_num) purchaseNum
FROM
tableName zp
WHERE
zp.purchase_day BETWEEN :beginTime AND :endTime
and user_code =:userCode
group by zp.commodity_code
order by purchaseMoney desc
<#if startIndex !=null && startIndex !="" && pageSize!=null && pageSize !="">
LIMIT :startIndex ,:pageSize
</#if>
*/ //排名","商品编号","商品名称","品类","品牌","采购金额(元)","采购数量(件)
if(org.apache.commons.collections.CollectionUtils.isNotEmpty(commodityListInfo)){
for (int i = 0; i < commodityListInfo.size(); i++) {
LinkedHashMap map = new LinkedHashMap();
QueryResultData queryResultData = commodityListInfo.get(i);
map.put("xuHao", (i+1) + "");
map.put("commodityCode", queryResultData.getCommodityCode());
map.put("commodityName", queryResultData.getCommodityName());
map.put("categoryName", queryResultData.getCategoryName());
map.put("brand", queryResultData.getBrand());
map.put("purchaseMoney", queryResultData.getPurchaseMoney());
map.put("purchaseNum", queryResultData.getPurchaseNum());
result.add(map);
}
}
return result;
}
}

DateUtil.java

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date; public class DateUtil {
public static final String YYYYMMDD = "yyyy-MM-dd";
public static final String YYYYMMDDHHMMSS = "yyyy-MM-dd HH:mm:ss";
public static final String YYYYMMDDHHmmss = "yyyy-MM-dd HH:mm:ss"; private DateUtil() { } public static Date parseDate(String time, String pattern) {
if (time == null) {
return null;
} else {
SimpleDateFormat df = new SimpleDateFormat(pattern); try {
return df.parse(time);
} catch (ParseException var4) {
return null;
}
}
} public static String formatDate(Date time, String pattern) {
if (time == null) {
return "";
} else {
String result = null;
DateFormat df = new SimpleDateFormat(pattern);
result = df.format(time);
return result;
}
}
}

getCellStyle方法

public static Map<String,CellStyle> getCellStyle(SXSSFWorkbook workbook){
Map<String,CellStyle> cellStyles=new HashMap<String,CellStyle>(); // 创建两种单元格格式
CellStyle cs = workbook.createCellStyle();
CellStyle cs2 = workbook.createCellStyle(); // 创建两种字体
Font f = workbook.createFont();
Font f2 = workbook.createFont(); // 创建第一种字体样式(用于列名)
f.setFontHeightInPoints((short) 10);
f.setColor(IndexedColors.BLACK.getIndex());
f.setBoldweight(Font.BOLDWEIGHT_BOLD); // 创建第二种字体样式(用于值)
f2.setFontHeightInPoints((short) 10);
f2.setColor(IndexedColors.BLACK.getIndex()); // 设置第一种单元格的样式(用于列名)
cs.setFont(f);
cs.setBorderLeft(CellStyle.BORDER_THIN);
cs.setBorderRight(CellStyle.BORDER_THIN);
cs.setBorderTop(CellStyle.BORDER_THIN);
cs.setBorderBottom(CellStyle.BORDER_THIN);
cs.setAlignment(CellStyle.ALIGN_CENTER); // 设置第二种单元格的样式(用于值)
cs2.setFont(f2);
cs2.setBorderLeft(CellStyle.BORDER_THIN);
cs2.setBorderRight(CellStyle.BORDER_THIN);
cs2.setBorderTop(CellStyle.BORDER_THIN);
cs2.setBorderBottom(CellStyle.BORDER_THIN);
cs2.setAlignment(CellStyle.ALIGN_CENTER);
cellStyles.put("cs",cs);
cellStyles.put("cs2",cs2);
return cellStyles;
}

七、SXSSFWorkbook生成大excle,避免内存溢出的更多相关文章

  1. 图片_ _Android有效解决加载大图片时内存溢出的问题 2

    Android有效解决加载大图片时内存溢出的问题 博客分类: Android Android游戏虚拟机算法JNI 尽量不要使用setImageBitmap或 setImageResource或 Bit ...

  2. Android开发中如何解决加载大图片时内存溢出的问题

    Android开发中如何解决加载大图片时内存溢出的问题    在Android开发过程中,我们经常会遇到加载的图片过大导致内存溢出的问题,其实类似这样的问题已经屡见不鲜了,下面将一些好的解决方案分享给 ...

  3. java 大数据处理之内存溢出解决办法(一)

    http://my.oschina.net/songhongxu/blog/209951 一.内存溢出类型 1.java.lang.OutOfMemoryError: PermGen space JV ...

  4. Android学习笔记_51_转android 加载大图片防止内存溢出

    首先来还原一下堆内存溢出的错误.首先在SD卡上放一张照片,分辨率为(3776 X 2520),大小为3.88MB,是我自己用相机拍的一张照片.应用的布局很简单,一个Button一个ImageView, ...

  5. Android有效解决加载大图片时内存溢出的问题

    首先,您需要了解一下,图片占用内存的计算方法,传送门:http://blog.csdn.net/scry5566/article/details/11568751 尽量不要使用setImageBitm ...

  6. MyEclipse中的Tomcat跑大项目时内存溢出:permgen space

    点击菜单栏的“Run”-"Run Configurations",在打开的窗口中点击“Arguments”选项卡. 在VM arguments中内容最下边(加上)输入:-Xms25 ...

  7. java使用stream流批量读取并合并文件,避免File相关类导致单文件过大造成的内存溢出。

    import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.F ...

  8. Android使用BitmapFactory.Options解决加载大图片内存溢出问题

    由于Android对图片使用内存有限制,若是加载几兆的大图片便内存溢出.Bitmap会将图片的所有像素(即长x宽)加载到内存中,如果图片分辨率过大,会直接导致内存溢出(java.lang.OutOfM ...

  9. 解决 VUE项目过大nodejs内存溢出问题

    今天在启动vue项目的时候报了这样一个错误, 如图所示:频繁出现此种情况,项目太大,导致内存溢出,排除代码问题外,可参照以下方式解决 // 全局安装increase-memory-limit npm ...

随机推荐

  1. MinGW dll导入导出类

    dll不仅可以导入导出函数,还可以导入导出类.这篇文章就来介绍如何将类导入dll中并导出. 首先我们建立一个名为dll.cpp的文件(又是这种破名字),里面写上: #include <iostr ...

  2. springboot中druid监控的配置(DruidConfiguration)

    当数据库连接池使用druid 时,我们进行一些简单的配置就能查看到sql监控,web监控,url监控等等. 以springboot为例,配置如下 import com.alibaba.druid.su ...

  3. 题解 SP5271 XOINC - A Coin Game

    SP5271 XOINC - A Coin Game 双倍经验:P2964 [USACO09NOV]硬币的游戏A Coin Game O3做法(TLE):枚举i,j,k,即剩下i枚金币,上一轮选了j枚 ...

  4. net core,redis的安装和试用

    一.window上面安装reids 在github上面下载,地址 安装完成后,进入安装目录打开redis服务, 这里双击Redis服务器即可启动Redis. 二.安装redis可视化工具 命令行操作r ...

  5. 线性筛-prime,强大O(n)

    和朴素的素数筛法一样,flag数组,记录x是否为素数 flag[x]=0,x为合数 falg[x]=1,x为素数 flag[1],无定义 其核心思想是,用x筛除与之差异最小的y,达到时间上O(n)的目 ...

  6. 6485. 【GDOI2020模拟02.25】沙塔斯月光

    题目描述 题解 镇♂男则反 如果没有操作3很好办,反着做维护操作1的次数即可 实际上一次操作3的贡献是zi*(∑i到操作1位置的距离) 维护一下即可O(n^4) code #include <b ...

  7. 寒假安卓app开发学习记录(3)

    今天终于开始正式的安卓软件开发学习.开始用了大约一个小时的时间把创建第一个软件的学习视频观看了一下.跟着视频一边学习一边操作. 首先是创建项目,创建的过程和之前创建Java项目的过程相似.先给app起 ...

  8. 【C语言】分别用下标法,地址法和指针法输出数组中的全部元素

    #include<stdio.h> int main() { ] = { ,,,, }; int i, * p; printf("下标法:\n"); ; i < ...

  9. 学会C#可以做什么

    C#基于.NET Framework  和 .NET CORE平台 Client/Server 客户端/服务端 windows桌面应用程序 winform  2D WPF  3D Browser/Se ...

  10. zookeeper集群搭建记录

    本文仅记录zookeeper集群搭建的过程,留待日后查看.使用. 一.硬件机器: 192.168.183.195 master-node 192.168.183.194 data-node1 192. ...