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。代码如下:

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

ExportExcelByPageUtil.java

  1. public static <T> SXSSFWorkbook makeSXSSFWorkbook(ReportExportExcelByPageService<T> exportExcelByPageTyService, T queryDataBo, String[] headers, int pageSize){
  2. int rowAccessWindowSize = 100;
  3. //这样表示SXSSFWorkbook只会保留100条数据在内存中,其它的数据都会写到磁盘里,这样的话占用的内存就会很少
  4. SXSSFWorkbook workbook = new SXSSFWorkbook(rowAccessWindowSize);
  5. SXSSFSheet sheet = (SXSSFSheet) workbook.createSheet("sheet1");
  6. Map<String,CellStyle> cellStyles=getCellStyle(workbook);
  7. CellStyle cs = cellStyles.get("cs");
  8. CellStyle cs2 = cellStyles.get("cs2");
  9.  
  10. //设置列名
  11. Row row = sheet.createRow((short) 0);
  12. for (int i = 0; i < headers.length; i++) {
  13. Cell cell = row.createCell(i);
  14. cell.setCellValue(headers[i]);
  15. cell.setCellStyle(cs);
  16. }
  17.  
  18. String pattern = "yyyy-MM-dd HH:mm:ss";
  19. String beginTime = DateUtil.formatDate(new Date(), pattern);
  20. int allCount = exportExcelByPageTyService.queryAllCount(queryDataBo);
  21. if(allCount <= 0){
  22. return workbook;
  23. }
  24.  
  25. //处理数据量超过一个sheet页最大量时异常,支持的最大导出数据量为10000
  26. int maxExportCount = 10000;
  27. allCount = allCount > maxExportCount ? maxExportCount : allCount;
  28. //page:查询总数据量为 allCount条记录时,每次生成pageSize条记录 需要page 次进行导出
  29. int page = (int) Math.ceil((double) allCount / (double) pageSize);
  30. //101条记录 aliquotFlag=false
  31. boolean aliquotFlag = allCount % pageSize == 0;
  32.  
  33. SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  34. for(int i = 0; i < page; i++){
  35. logger.error("第"+(i+1)+"次循环开始:"+ DateUtil.formatDate(new Date(), pattern));
  36. int startIndex = i*pageSize;
  37. int maxCount = pageSize;
  38. //最后一次的最大记录数
  39. if(!aliquotFlag && i==(page-1)){
  40. maxCount = allCount - startIndex;
  41. }
  42. logger.error("导出分页:startIndex==>"+startIndex+", pageSize==>"+maxCount);
  43. List<String> ids = exportExcelByPageTyService.queryIdsInfo(queryDataBo, startIndex, maxCount);
  44. List<LinkedHashMap<String, Object>> dataList = exportExcelByPageTyService.queryDatas(ids);
  45. if(CollectionUtils.isNotEmpty(dataList)){
  46. Set<Map.Entry<String, Object>> set = null;
  47. Iterator<Map.Entry<String, Object>> iterator = null;
  48. int j = 0;
  49. for (int rowNum = 1, len = dataList.size() + 1; rowNum < len; rowNum++) {
  50. Row row1 = sheet.createRow(startIndex+rowNum);
  51. set = dataList.get(rowNum - 1).entrySet();
  52. iterator = set.iterator();
  53. j = 0;
  54. while (iterator.hasNext()) {
  55. Map.Entry<String, Object> entry = (Map.Entry<String, Object>) iterator.next();
  56. String cellValue = "";
  57. if(entry.getValue()==null){
  58. cellValue = "";
  59. }else if(entry.getValue() instanceof Timestamp){
  60. cellValue = sdf.format((Timestamp) entry.getValue());
  61. } else {
  62. cellValue = String.valueOf(entry.getValue());
  63. }
  64. Cell cell = row1.createCell(j);
  65. cell.setCellValue(cellValue);
  66. cell.setCellStyle(cs2);
  67. j++;
  68. }
  69. if (rowNum % rowAccessWindowSize == 0) {
  70. try {
  71. sheet.flushRows();
  72. } catch (IOException e) {
  73. logger.error("sheet从内存写入本地硬盘失败", e);
  74. }
  75. }
  76. }
  77. }
  78. dataList = null;
  79. logger.error("第"+(i+1)+"次循环结束:"+formatDate(new Date(), pattern));
  80. }
  81. logger.error("导出分页开始:"+beginTime);
  82. logger.error("导出分页结束:"+ formatDate(new Date(), pattern));
  83. return workbook;
  84. }
  1. public static boolean exportExcel(HttpServletRequest request, HttpServletResponse response,SXSSFWorkbook workbook, String fileName){
  2. OutputStream out = null;
  3. try {
  4. String userAgent = request.getHeader("USER-AGENT");
  5. fileName = dealChineseFileName(userAgent, fileName);
  6. out = response.getOutputStream();
  7. response.setContentType("application/x-download");
  8. response.setHeader("Pragma", "public");
  9. response.setHeader("Cache-Control", "max-age=30");
  10. response.setHeader("Content-disposition", "attachment;filename=" + fileName);
  11. workbook.write(out);
  12. out.flush();
  13. } catch (Exception e) {
  14. logger.error("导出Excel文件失败", e);
  15. return false;
  16. }finally {
  17. if (out != null) {
  18. try {
  19. out.close();
  20. } catch (IOException e) {
  21. logger.error("导出Excel文件关闭输出流失败", e);
  22. } finally {
  23. out = null;
  24. }
  25. }
  26. if(workbook!=null){
  27. try {
  28. workbook.dispose();
  29. workbook.close();
  30. } catch (IOException e) {
  31. logger.error("导出Excel文件关闭输出流失败", e);
  32. } finally {
  33. workbook = null;
  34. }
  35. }
  36. }
  37. return true;
  38. }

ReportExportExcelByPageService.java

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

ReportExportExcelByPageServiceImpl.java

  1. public class ReportExportExcelByPageServiceImpl implements ReportExportExcelByPageService<QueryParamDto> {
  2. protected Logger logger = LoggerFactory.getLogger(this.getClass());
  3.  
  4. @Override
  5. public int queryAllCount(QueryParamDto queryDataBo) {
  6. //查询记录数业务代码
  7. }
  8.  
  9. @Override
  10. public List<String> queryIdsInfo(QueryParamDto queryDataBo, int startIndex, int pageSize) {
  11. List<String> list = new ArrayList<String>();
  12. list.add(queryDataBo.getBeginTime());
  13. list.add(queryDataBo.getEndTime());
  14. list.add(queryDataBo.getUserCode());
  15. list.add(queryDataBo.getType());
  16. list.add(queryDataBo.getAllType());
  17. list.add(String.valueOf(startIndex));
  18. list.add(String.valueOf(pageSize));
  19. return list;
  20. }
  21.  
  22. @Override
  23. public List<LinkedHashMap<String, Object>> queryDatas(final List<String> ids) {
  24. List<LinkedHashMap<String, Object>> result = new ArrayList<>();
  25. if (CollectionUtils.isEmpty(ids)) {
  26. return result;
  27. }
  28. QueryParamDto queryDataBo=new QueryParamDto();
  29. queryDataBo.setBeginTime( ids.get(0));
  30. queryDataBo.setEndTime(ids.get(1));
  31. queryDataBo.setUserCode(ids.get(2));
  32. queryDataBo.setType(ids.get(3));
  33. queryDataBo.setAllType(ids.get(4));
  34. queryDataBo.setStartIndex(Integer.parseInt(ids.get(5)));
  35. queryDataBo.setPageSize(Integer.parseInt(ids.get(6)));
  36.  
  37. //根据分页的入参进行分页查询:返回结果为list
  38. List<QueryResultData> commodityListInfo = ...;
  39. /*
  40. SELECT
  41. zp.commodity_code commodityCode,
  42. zp.commodity_name commodityName,
  43. zp.category_name categoryName,
  44. zp.brand brand,
  45. sum(zp.purchase_money) purchaseMoney,
  46. sum(zp.purchase_num) purchaseNum
  47. FROM
  48. tableName zp
  49. WHERE
  50. zp.purchase_day BETWEEN :beginTime AND :endTime
  51. and user_code =:userCode
  52. group by zp.commodity_code
  53. order by purchaseMoney desc
  54. <#if startIndex !=null && startIndex !="" && pageSize!=null && pageSize !="">
  55. LIMIT :startIndex ,:pageSize
  56. </#if>
  57. */
  58.  
  59. //排名","商品编号","商品名称","品类","品牌","采购金额(元)","采购数量(件)
  60. if(org.apache.commons.collections.CollectionUtils.isNotEmpty(commodityListInfo)){
  61. for (int i = 0; i < commodityListInfo.size(); i++) {
  62. LinkedHashMap map = new LinkedHashMap();
  63. QueryResultData queryResultData = commodityListInfo.get(i);
  64. map.put("xuHao", (i+1) + "");
  65. map.put("commodityCode", queryResultData.getCommodityCode());
  66. map.put("commodityName", queryResultData.getCommodityName());
  67. map.put("categoryName", queryResultData.getCategoryName());
  68. map.put("brand", queryResultData.getBrand());
  69. map.put("purchaseMoney", queryResultData.getPurchaseMoney());
  70. map.put("purchaseNum", queryResultData.getPurchaseNum());
  71. result.add(map);
  72. }
  73. }
  74. return result;
  75. }
  76. }

DateUtil.java

  1. import java.text.DateFormat;
  2. import java.text.ParseException;
  3. import java.text.SimpleDateFormat;
  4. import java.util.Date;
  5.  
  6. public class DateUtil {
  7. public static final String YYYYMMDD = "yyyy-MM-dd";
  8. public static final String YYYYMMDDHHMMSS = "yyyy-MM-dd HH:mm:ss";
  9. public static final String YYYYMMDDHHmmss = "yyyy-MM-dd HH:mm:ss";
  10.  
  11. private DateUtil() {
  12.  
  13. }
  14.  
  15. public static Date parseDate(String time, String pattern) {
  16. if (time == null) {
  17. return null;
  18. } else {
  19. SimpleDateFormat df = new SimpleDateFormat(pattern);
  20.  
  21. try {
  22. return df.parse(time);
  23. } catch (ParseException var4) {
  24. return null;
  25. }
  26. }
  27. }
  28.  
  29. public static String formatDate(Date time, String pattern) {
  30. if (time == null) {
  31. return "";
  32. } else {
  33. String result = null;
  34. DateFormat df = new SimpleDateFormat(pattern);
  35. result = df.format(time);
  36. return result;
  37. }
  38. }
  39. }

getCellStyle方法

  1. public static Map<String,CellStyle> getCellStyle(SXSSFWorkbook workbook){
  2. Map<String,CellStyle> cellStyles=new HashMap<String,CellStyle>();
  3.  
  4. // 创建两种单元格格式
  5. CellStyle cs = workbook.createCellStyle();
  6. CellStyle cs2 = workbook.createCellStyle();
  7.  
  8. // 创建两种字体
  9. Font f = workbook.createFont();
  10. Font f2 = workbook.createFont();
  11.  
  12. // 创建第一种字体样式(用于列名)
  13. f.setFontHeightInPoints((short) 10);
  14. f.setColor(IndexedColors.BLACK.getIndex());
  15. f.setBoldweight(Font.BOLDWEIGHT_BOLD);
  16.  
  17. // 创建第二种字体样式(用于值)
  18. f2.setFontHeightInPoints((short) 10);
  19. f2.setColor(IndexedColors.BLACK.getIndex());
  20.  
  21. // 设置第一种单元格的样式(用于列名)
  22. cs.setFont(f);
  23. cs.setBorderLeft(CellStyle.BORDER_THIN);
  24. cs.setBorderRight(CellStyle.BORDER_THIN);
  25. cs.setBorderTop(CellStyle.BORDER_THIN);
  26. cs.setBorderBottom(CellStyle.BORDER_THIN);
  27. cs.setAlignment(CellStyle.ALIGN_CENTER);
  28.  
  29. // 设置第二种单元格的样式(用于值)
  30. cs2.setFont(f2);
  31. cs2.setBorderLeft(CellStyle.BORDER_THIN);
  32. cs2.setBorderRight(CellStyle.BORDER_THIN);
  33. cs2.setBorderTop(CellStyle.BORDER_THIN);
  34. cs2.setBorderBottom(CellStyle.BORDER_THIN);
  35. cs2.setAlignment(CellStyle.ALIGN_CENTER);
  36. cellStyles.put("cs",cs);
  37. cellStyles.put("cs2",cs2);
  38. return cellStyles;
  39. }

七、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. C++算法导论第九章O(n)期望选择序列第i小的数字

    #include<iostream> #include<vector> #include<algorithm> #include<time.h> usi ...

  2. IntelliJ IDEA 2017.3尚硅谷-----设置字体大小行间距

  3. Java进阶学习(5)之设计原则(下)

    框架加数据 把数据的硬编码尽可能解成框架加数据的结构 城堡游戏修改后的代码 Room类 package com.castle; import java.util.HashMap; public cla ...

  4. python+pygame的导弹追踪鼠标游戏设置和说明

    1.效果图 2.注意事项,代码里有说明 3.完整的代码 #导出模块 import pygame,sys from math import * #设置RESIZABLE前,必须导出下面的模块,否则报错 ...

  5. Educational Codeforces Round 80 (Rated for Div. 2)C(DP)

    #define HAVE_STRUCT_TIMESPEC #include<bits/stdc++.h> using namespace std; ; ][],temp[][]; int ...

  6. bash_profile文件

    bash_profile文件的作用 如何填写 如何生效

  7. BufferedInputStream 介绍

    BufferedInputStream 介绍 BufferedInputStream 是缓冲输入流.它继承于FilterInputStream.BufferedInputStream 的作用是为另一个 ...

  8. UDP协议 sendto 和 recvfrom 浅析与示例

    UDP(user datagram protocol)用户数据报协议,属于传输层. UDP是面向非连接的协议,它不与对方建立连接,而是直接把数据报发给对方.UDP无需建立类如三次握手的连接,使得通信效 ...

  9. redis 字符串操作

    redis 字符串创建SET操作 127.0.0.1:6379> set number "10086" OK 127.0.0.1:6379> set book &quo ...

  10. python 的集合

    set  的特点: 把不同的元素组合在一起,元素值必须是可哈希不可变的 set 的创建 s = set ('alex li') print(s) 表现形式:去重 {'e', 'i', ' ', 'l' ...