注意使用 SXSSFWorkbook 此类在构造表格和处理行高的时候效率极高,刚开始时我使用的 XSSFWorkbook 就出现构造表格效率极低,一万行基本需要3秒左右,那当导出百万级数据就慢的要死啦,而且他会让内存溢出

POI3.8的SXSSF包是XSSF的一个扩展版本,支持流处理,在生成大数据量的电子表格且堆空间有限时使用。SXSSF通过限制内存中可访问的记录行数来实现其低内存利用,当达到限定值时,新一行数据的加入会引起老一行的数据刷新到硬盘。

比如内存中限制行数为100,当行号到达101时,行号为0的记录刷新到硬盘并从内存中删除,当行号到达102时,行号为1的记录刷新到硬盘,并从内存中删除,以此类推。

rowAccessWindowSize代表指定的内存中缓存记录数,默认为100,此值可以通过

new SXSSFWorkbook(int rowAccessWindowSize)或SXSSFSheet.setRandomAccessWindowSize(intwindowSize)来设置。

SXSSF在把内存数据刷新到硬盘时,是把每个SHEET生成一个临时文件,这个临时文件可能会很大,有可以会达到G级别,如果文件的过大对你来说是一个问题,你可以使用下面的方法让SXSSF来进行压缩,当然性能也会有一定的影响。

SXSSFWorkbook wb = new SXSSFWorkbook();               wb.setCompressTempFiles(true);

临时文件:

SXSSF在导出的过程中会生成两种临时文件,一种是为每个sheet页生成一个 xml 临时文件,一种是最终导出时生成的完整.xlsx 文件

临时文件所在位置:

windows: C盘下的 AppData\Local\Temp\poifiles    例如我的:C:\Users\011336\AppData\Local\Temp\poifiles

Linux:在Tomcat目录下的 temp/poifiles

临时文件的删除:

workbook.write(fileOut);     write()方法中包含删除 .xlsx 文件的方法,在它的finally代码块里,具体可以去查看源码

workbook.dispose();          dispose()方法就是用来删除那些 xml 格式的临时文件的

需要注意的细节:

每创建完一个sheet页就会生成一个xml文件  但是所有的 xml 文件都是空的,只有调用workbook.write(fileOut); 方法时,才会往xml中写数据,也就是说之前构造的几百万数据都在内存中,这是很危险的行为,当达到一定量时可能就会有内存溢出的风险,所以要记得在每个sheet页构造完成之后都手动把数据刷到磁盘当中((SXSSFSheet)sheet).flushRows();其实write()方法中也是for循环调用的flushRows()方法。

最关键的点:

  记得点赞哦...

1.  CommentController

/*** excel导出功能* @param commentSearch* @param response* @param request* @return* @throws Exception*/@RequestMapping("/exportCommentInfo")@ResponseBody@NoRepeatRequestpublic BaseDTO exportCommentInfo(CommentSearch commentSearch, HttpServletResponse response, HttpServletRequest request) throws Exception{LOGGER.info("CommentController.exportCommentInfo start");long startTime = System.currentTimeMillis();LOGGER.info("开始下载.........................................");List<ErrorInfo> errors = null;int result = 0;String fileName = FileNameUtils.getExportCommontExcelFileName();OutputStream fileOut  = null;SXSSFWorkbook workbook = null;try {LOGGER.debug("classpath: " + fileName);workbook = new SXSSFWorkbook(10000);//内存中实时存在10000个对象,超过的实时写入磁盘,保证内存消耗不会过大commentService.exportCommentInfo(request,workbook, commentSearch);// 定义excel文件名response.setCharacterEncoding("UTF-8");response.setHeader("Content-Disposition", "attachment; filename=\""+ URLEncoder.encode(fileName, "UTF-8") + "\"");

        // 定义输出流
        fileOut = response.getOutputStream();
        // 调用导出方法生成最终的 poi-sxssf-template.xlsx 临时文件,并且此方法包含删除此临时文件的方法
        workbook.write(fileOut);
        // 此方法能够删除导出过程中生成的xml临时文件
        workbook.dispose();

        } catch (Exception e) { LOGGER.error("InterfaceInfoController.exportInterfaceInfo Exception: ", e);ErrorInfo errorInfo = new ErrorInfo("system.error", "系统异常!");errors = Arrays.asList(errorInfo);}finally {workbook.close();}LOGGER.info("下载完成....|||||.......用时:" + (System.currentTimeMillis() - startTime));return tranferBaseDTO(errors, result);}

2.导出实现类  exportCommentInfo

/*** excel 导出* @param: [request, workbook, commentSearch]* @return: void* @auther: 011336* @date: 2018/12/7 15:03*/@Overridepublic void exportCommentInfo(HttpServletRequest request, SXSSFWorkbook workbook, CommentSearch commentSearch) {//excel样式CellStyle centerStyle = workbook.createCellStyle();CellStyle cellStyleCenter = workbook.createCellStyle();CellStyle cellStyleLeft = workbook.createCellStyle(); cellStyleCenter.setAlignment(HSSFCellStyle.ALIGN_CENTER); //水平布局:居中cellStyleCenter.setVerticalAlignment(CellStyle.VERTICAL_CENTER);//垂直居中 cellStyleCenter.setWrapText(true);cellStyleLeft.setAlignment(HSSFCellStyle.ALIGN_LEFT); //水平布局:居左cellStyleLeft.setVerticalAlignment(CellStyle.VERTICAL_CENTER);//垂直居中cellStyleLeft.setWrapText(true);
 Font font =workbook.createFont();font.setColor(Font.COLOR_NORMAL); //字体颜色 font.setFontName("黑体"); //字体font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD); //宽度// 设置标题单元格类型
        centerStyle.setFont(font);centerStyle.setAlignment(HSSFCellStyle.ALIGN_CENTER); //水平布局:居中 centerStyle.setWrapText(true);centerStyle.setAlignment(XSSFCellStyle.ALIGN_CENTER);centerStyle.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);//设置前景填充样式centerStyle.setFillForegroundColor(HSSFColor.GREY_50_PERCENT.index);//前景填充色CreationHelper createHelper = workbook.getCreationHelper();// 设置标题边框
        centerStyle.setBorderBottom(HSSFCellStyle.BORDER_THIN);centerStyle.setBorderLeft(HSSFCellStyle.BORDER_THIN);centerStyle.setBorderRight(HSSFCellStyle.BORDER_THIN);centerStyle.setBorderTop(HSSFCellStyle.BORDER_THIN);//分批导出int totalCount = countCommentNum(commentSearch);//如果导出数据量大于   设定的最大数据量    则最多不能超过设定的数量if(pageNumExport != null && totalCount > pageNumExport*limitExport){totalCount = pageNumExport*limitExport;}int number = (totalCount % limitExport) == 0 ? totalCount/ limitExport : totalCount / limitExport + 1;List<CommentVo> commentVOs = new ArrayList<>();for (int i = 0; i < number; i++) {long startTime = System.currentTimeMillis();LOGGER.info("【第"+i+"】次开始查询数据库.........................................");commentVOs = getCommentVoExport(commentSearch, i*limitExport, limitExport);LOGGER.info("【第"+i+"】次数据库查询完成....|||||.......用时:" + (System.currentTimeMillis() - startTime));int page = i+1;Sheet sheet = workbook.createSheet("评论清单"+( page<10 ? "0"+page : page ) );batchExport(request,sheet, commentSearch, commentVOs, centerStyle, cellStyleCenter, cellStyleLeft);

        try {
          ((SXSSFSheet)sheet).flushRows();//每创建完成一个sheet页就把数据刷新到磁盘
        } catch (IOException e) {
          LOGGER.error("CommentServiceImpl.exportCommentInfo flushRows() exception ,that is not important"+e);
          e.printStackTrace();
        }

commentVOs.clear(); } }-----提示:如果这里不手动刷的话,当所有sheet页都刷到内存中后,workbook.write()方法会采用for循环把所有的数据都刷到磁盘中,也就是说,如果这里不手动刷,那么所有的对象就都在内存中。实测表明 即使它已经创建的xml临时文件,此时所有的xml临时文件也是空的,都只能通过最后的workbook.write()方法统一刷新到磁盘。那么就可能会有内存溢出的风险

3.   batchExport(request,sheet, commentSearch, commentVOs, centerStyle, cellStyleCenter, cellStyleLeft); 的实现
/*** 构造excel,赋值,样式* @param: [request, sheet, commentSearch, commentVOs, centerStyle, cellStyleCenter, cellStyleLeft]* @return: void* @auther: 011336* @date: 2018/12/7 15:20*/public void batchExport(HttpServletRequest request,Sheet sheet , CommentSearch commentSearch,List<CommentVo> commentVOs,CellStyle centerStyle,CellStyle cellStyleCenter,CellStyle cellStyleLeft){if (CollectionUtils.isEmpty(commentVOs)) {LOGGER.debug("exportCommentInfo finish: " + commentVOs);}Row newRowOfInparamTitle = sheet.createRow(0);String[] headerOfInParam = { "序号", "评价日期", "来源", "星级", "评论内容", "类型一级", "类型二级", "类型三级", "情感识别","建议类","订单号", "航班号", "航班日期","订单联系人","联系电话","备注"};for (int j = 0; j < headerOfInParam.length; j++) {newRowOfInparamTitle.createCell(j);}for (int j = 0; j < headerOfInParam.length; j++) {sheet.getRow(0).getCell(j).setCellValue(new XSSFRichTextString(headerOfInParam[j]));sheet.getRow(0).getCell(j).setCellStyle(centerStyle);}long startTime = System.currentTimeMillis();LOGGER.info("构造表格开始.........................................");for (int i = 0; i < commentVOs.size(); i++) {CommentVo commentVo = commentVOs.get(i);int index = i + 1;Row createRow = sheet.createRow(i + 1);for (int j = 0; j < headerOfInParam.length; j++) {createRow.createCell(j);}sheet.getRow(i + 1).getCell(0).setCellValue(index + "");sheet.getRow(i + 1).getCell(0).setCellStyle(cellStyleCenter);sheet.getRow(i + 1).getCell(1).setCellValue(dealTrim(dateToStr2(commentVo.getCmtTime())));sheet.getRow(i + 1).getCell(1).setCellStyle(cellStyleCenter);sheet.getRow(i + 1).getCell(2).setCellValue(dealTrim(commentVo.getTerminalId()));sheet.getRow(i + 1).getCell(2).setCellStyle(cellStyleCenter);sheet.getRow(i + 1).getCell(3).setCellValue(dealTrim(commentVo.getCmtLevel()));sheet.getRow(i + 1).getCell(3).setCellStyle(cellStyleCenter);sheet.getRow(i + 1).getCell(4).setCellValue(dealTrim(commentVo.getCmtText()));sheet.getRow(i + 1).getCell(4).setCellStyle(cellStyleLeft);sheet.getRow(i + 1).getCell(5).setCellValue(dealTrim(dealTrim(commentVo.getClassfyFirst())));sheet.getRow(i + 1).getCell(5).setCellStyle(cellStyleLeft);sheet.getRow(i + 1).getCell(6).setCellValue(dealTrim(commentVo.getClassfySecond()));sheet.getRow(i + 1).getCell(6).setCellStyle(cellStyleLeft);sheet.getRow(i + 1).getCell(7).setCellValue(dealTrim(commentVo.getClassfyThird()));sheet.getRow(i + 1).getCell(7).setCellStyle(cellStyleLeft);String emotion = commentVo.getEmotion();if("0".equals(emotion)){emotion="差评";}else if("1".equals(emotion)){emotion="好评";}sheet.getRow(i + 1).getCell(8).setCellValue(dealTrim(emotion));sheet.getRow(i + 1).getCell(8).setCellStyle(cellStyleCenter);String isSuggestion = commentVo.getIsSuggestion();if("0".equals(isSuggestion)){isSuggestion="否";}else if("1".equals(isSuggestion)){isSuggestion="是";}sheet.getRow(i + 1).getCell(9).setCellValue(dealTrim(isSuggestion));sheet.getRow(i + 1).getCell(9).setCellStyle(cellStyleCenter);sheet.getRow(i + 1).getCell(10).setCellValue(dealTrim(commentVo.getOrderNo()));sheet.getRow(i + 1).getCell(10).setCellStyle(cellStyleCenter);sheet.getRow(i + 1).getCell(11).setCellValue(dealTrim(commentVo.getFlightNo()));sheet.getRow(i + 1).getCell(11).setCellStyle(cellStyleCenter);sheet.getRow(i + 1).getCell(12).setCellValue(dateToStr(commentVo.getFlightDate()));sheet.getRow(i + 1).getCell(12).setCellStyle(cellStyleCenter);sheet.getRow(i + 1).getCell(13).setCellValue(dealTrim(commentVo.getcName()));sheet.getRow(i + 1).getCell(13).setCellStyle(cellStyleLeft);sheet.getRow(i + 1).getCell(14).setCellValue(dealTrim(commentVo.getcTel()));sheet.getRow(i + 1).getCell(14).setCellStyle(cellStyleLeft);sheet.getRow(i + 1).getCell(15).setCellValue(dealTrim(commentVo.getRemark()));sheet.getRow(i + 1).getCell(15).setCellStyle(cellStyleLeft);if(i%1000 == 0){request.getSession().setAttribute("currentNum",i+1);}}LOGGER.info("构造表格结束....|||||.......用时:" + (System.currentTimeMillis() - startTime));long startTime2 = System.currentTimeMillis();LOGGER.info("处理行高开始.........................................");dealColumWidth( headerOfInParam, sheet,commentVOs, request);LOGGER.info("处理行高结束....|||||.......用时:" + (System.currentTimeMillis() - startTime2));}

4.  处理行高代码的实现   dealColumWidth( headerOfInParam, sheet,commentVOs, request);这里主要是设置固定列宽,然后自适应行高,二期自适应行高他是不提供方法的,只能自己去计算

 /*** 设置列宽* @param: [headerOfInParam, sheet, commentVOs, request]* @return: void* @auther: 011336* @date: 2018/12/7 15:20*/public void dealColumWidth(String[] headerOfInParam,Sheet sheet,List<CommentVo> commentVOs,HttpServletRequest request){//单独处理   评论内容  和  评论备注  以及 分类   的宽度 和高度sheet.setColumnWidth(1, 5500);//11个汉字  评价日期sheet.setColumnWidth(2, 1500);//3个汉字   来源sheet.setColumnWidth(3, 1500);//3个汉字   星级sheet.setColumnWidth(4, 12000);//24个汉字   评论sheet.setColumnWidth(5, 3000);//6个汉字   分类1sheet.setColumnWidth(6, 3000);//6个汉字   分类2sheet.setColumnWidth(7, 4000);//8个汉字   分类3sheet.setColumnWidth(8, 2500);//5个汉字   情感识别sheet.setColumnWidth(9, 2000);//4个汉字   建议类sheet.setColumnWidth(10, 2500);//5个汉字   订单号sheet.setColumnWidth(11, 2500);//5个汉字   航班号sheet.setColumnWidth(12, 3500);//7个汉字   航班日期sheet.setColumnWidth(13, 4000);//8个汉字   订单联系人sheet.setColumnWidth(14, 4000);//8个汉字   联系电话sheet.setColumnWidth(15, 10000);//20个汉字  备注double rn = 20.00 , cn = 24.00 ,c1 = 6.00 ,c2 = 6.00 ,c3 = 8.00 , nn = 8.00;//ca应该为20可是导出结果却换行不对所以改成16保险一点//计算  设置自定义行高for (int i = 0; i < commentVOs.size(); i++) {//计算行高CommentVo commentVo = commentVOs.get(i);int charNumC1 =  (int)Math.ceil( dealTrim(commentVo.getClassfyFirst()).length() / c1 );//分类1int charNumC2 =  (int)Math.ceil( dealTrim(commentVo.getClassfySecond()).length() / c2 );//分类2int charNumC3 =  (int)Math.ceil( dealTrim(commentVo.getClassfyThird()).length() / c3 );//分类2int charNumRemark =  (int)Math.ceil( dealTrim(commentVo.getRemark()).length() / rn );//备注int charNumComent =   (int)Math.ceil( dealTrim(commentVo.getCmtText()).length() / cn);//评论int charNumName =   (int)Math.ceil( dealTrim(commentVo.getcName()).length() / nn);//订单联系人List<Integer> input = new ArrayList<Integer>();//input.add(charNum+num);//charNum 是分类的条数,每条一行。num是分类字数大于20的情况就多分配一行
            input.add(charNumC1);input.add(charNumC2);input.add(charNumC3);input.add(charNumRemark);input.add(charNumComent);input.add(charNumName);int rowNum = Collections.max(input);Row row = sheet.getRow(i+1);row.setHeight((short)(sheet.getDefaultRowHeight()*rowNum));} request.getSession().setAttribute("currentNum",commentVOs.size());}

代码中的request可忽略,那是我做的进度条,

转载于:https://www.cnblogs.com/UncleWang001/p/10106859.html

poi实现百万级数据导出相关推荐

  1. php直接导出csv,PHP百万级数据导出方案(生成器直接输出单个CSV)

    概述: 之前写过一篇文章叫PHP百万级数据导出方案(多csv文件压缩),发现很多朋友都很感兴趣,但是当时用的方法比较不方便,可能不太符合很多人的需求.后来想了一下如何优化时,了解到能用生成器来处理内存 ...

  2. java excel 使用ExcelWriter 百万级数据导出

    一.Java 通过hutool工具类ExcelWriter 导出 运用到多线程分页查询 这个采用的是Java的utool工具类ExcelWriter 导出 踩过一些坑,尽量用一条sql 将所有数据查询 ...

  3. PHP如何实现百万级数据导出

    公司目前有一个需求,需要对一个日增量在20万+数据量的数据表中的数据进行可自定义条件筛选的导出数据,该功能需要对多个部门进行开发使用,要保证功能可用的前提下,尽量优化体验. 首先介绍一下当前可利用的资 ...

  4. 阿里开源百万级数据导出Excel表格 三步简单导出 附官方文档

    阿里巴巴Excel导出优化速度 ,64M内存20秒读取75M(46W行25列)的Excel(3.0.2+版本) 官方文档:EasyExcel · 语雀EasyExcel是一个基于Java的简单.省内存 ...

  5. Java 亿万级数据导出到Excel

    由于项目需要,处理百万级数据导出问题. 直接开始撸代码~ 导入依赖 <!-- 文件导出--><dependency><groupId>org.apache.poi& ...

  6. springboot+poi导出百万级数据避免OOM内存溢出

    springboot+poi导出百万级数据避免OOM内存溢出 文章目录 springboot+poi导出百万级数据避免OOM内存溢出 前言 一.具体实现 二.代码实现 1.引入poi包 2.功能代码 ...

  7. 百万级数据的导出解决方案

    一.传统POI的的版本优缺点比较 首先我们知道POI中我们最熟悉的莫过于WorkBook这样一个接口,我们的POI版本也在更新的同时对这个几口的实现类做了更新: HSSFWorkbook : 这个实现 ...

  8. POI报表及百万数据导入导出方案

    1.POI报表的概述 1.1 需求说明 在企业级应用开发中,Excel报表是一种最常见的报表需求.Excel报表开发一般分为两种形式:为了方便操作,基于Excel的报表批量上传数据:通过java代码生 ...

  9. POI报表入门及百万数据报表导出和读取

    POI报表概述 需求说明 在企业级应用开发中,Excel报表是一种最常见的报表需求.Excel报表开发一般分为两种形式: 为了 方便操作,基于Excel的报表批量上传数据 通过java代码生成Exce ...

  10. easyexcel导出百万级数据_百万级数据下的mysql深度解析

    点击蓝字关注我们!每天获取最新的编程小知识! 源 / php中文网      源 / www.php.cn mysql 作为一款非常优秀的免费数据库被广泛的使用,平时我们开发的项目数据过百万的时候不多 ...

最新文章

  1. Weblogic12C 集群实现session同步
  2. 转:给自己TopCoder SRM的建议
  3. iOS学习之UItableView
  4. eclipse linux windows 乱码,Ubuntu下Eclipse打开Windows下的工程文件乱码解决办法
  5. html5引擎笔试题,最新!HTML5经典面试题型(附答案)
  6. js中立即执行函数会预编译吗_JavaScript预编译过程
  7. 微信你scope 参数错误 php,微信开发: scope参数错误或没有scope权限解决方法
  8. python对编写神经网络作用_神经网络(BP)算法Python实现及应用
  9. [CF1093G] Multidimensional Queries
  10. iOS NSString的常用用法
  11. 时不变线性系统和时变线性系统方程的对角化
  12. 关于使用Curvy插件实现物体沿指定路径移动的方法
  13. android 根据屏幕大小自行选择图片
  14. jsoup爬取豆瓣电影top250
  15. hbuilder 断点_Hbuilder配置php断点调试
  16. redhat linux光盘4,技巧:把3张Redhat Linux 9的安装光盘刻录到一张DVD光盘中
  17. Class.getEnclosingClass 和 Class.getDeclaringClass
  18. 币圈里,大佬们都在用的网站和APP都在这
  19. 服务器硬盘ahci,Windows2008 AHCI功能开启方法(提升硬盘加速)
  20. 第五周嵌入式课程总结

热门文章

  1. Eigen中的map
  2. 网络流24题 洛谷 2763 试题库问题
  3. 19_debug断点调试
  4. 关于Hbase的cache配置
  5. Redis内存分配简单分析
  6. VS2005 中网站转为Web应用程序的方法
  7. PetShop数据访问层之消息处理(转Bruce Zhang)
  8. QCache 缓存(类似于map的模板类,逻辑意义上的缓存Cache,方便管理,默认类似于LRU的淘汰算法)...
  9. MySQL中的float和decimal类型有什么区别
  10. Redis常见配置文件详解