java实现百万级大数据量导出

实现方式一
一,分析
excel导出如果数据量过大,会出现两个内存溢出的问题

  1. 查绚数据量过大,导致内存溢出(可通过分批查绚解决)
  2. 下载的时候大EXCEL转换的输出流内存溢出;(该方式可以通过新版的SXSSFWorkbook来解决,可通过其构造函数执指定在内存中缓存的行数,剩余的会自动缓存在硬盘的临时目录上,同时,并不会存在页面卡顿的情况);
  3. 为了能够使用不同的mapper并分批写数据, 采用了外观模式和模板方法模式,大体分三步:
    a. 根据总数量生成excel,确定sheet的数量和写标题;
    b. 写数据,在可变的匿名内部类中实现写入逻辑;
    c. 转换输出流进行下载;

二,代码实现

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

如果是springboot2.0,则不需要poi依赖,如果是1.0,则需要poi依赖,并且poi和poi-ooxml的版本要保持一致

application.properties

spring.mvc.view.prefix=/jsp/
spring.mvc.view.suffix=.jspspring.datasource.url=jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.jdbc.Driverspring.jpa.database=MYSQL
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none

ExcelConstant

package com.example.util;public class ExcelConstant {/*** 每个sheet存储的记录数 100W*/public static final Integer PER_SHEET_ROW_COUNT = 1000000;/*** 每次向EXCEL写入的记录数(查询每页数据大小) 20W*/public static final Integer PER_WRITE_ROW_COUNT = 200000;/*** 每个sheet的写入次数 5*/public static final Integer PER_SHEET_WRITE_COUNT = PER_SHEET_ROW_COUNT / PER_WRITE_ROW_COUNT;}

写数据的委托类

package com.example.util;import org.apache.poi.xssf.streaming.SXSSFSheet;/*** @description EXCEL写数据委托类*/
public interface WriteExcelDataDelegated {/*** EXCEL写数据委托类  针对不同的情况自行实现** @param eachSheet     指定SHEET* @param startRowCount 开始行* @param endRowCount   结束行* @param currentPage   分批查询开始页* @param pageSize      分批查询数据量* @throws Exception*/public abstract void writeExcelData(SXSSFSheet eachSheet, Integer startRowCount, Integer endRowCount, Integer currentPage, Integer pageSize) throws Exception;}

dateutil工具类

package com.example.util;import java.text.SimpleDateFormat;
import java.util.Date;public class DateUtil {public static final String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";/*** 将日期转换为字符串** @param date   DATE日期* @param format 转换格式* @return 字符串日期*/public static String formatDate(Date date, String format) {SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format);return simpleDateFormat.format(date);}}

poi工具类

package com.example.util;import org.apache.poi.xssf.streaming.SXSSFCell;
import org.apache.poi.xssf.streaming.SXSSFRow;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import javax.servlet.http.HttpServletResponse;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Date;public class PoiUtil {private final static Logger logger = LoggerFactory.getLogger(PoiUtil.class);/*** 初始化EXCEL(sheet个数和标题)** @param totalRowCount 总记录数* @param titles        标题集合* @return XSSFWorkbook对象*/public static SXSSFWorkbook initExcel(Integer totalRowCount, String[] titles) {// 在内存当中保持 100 行 , 超过的数据放到硬盘中在内存当中保持 100 行 , 超过的数据放到硬盘中SXSSFWorkbook wb = new SXSSFWorkbook(100);Integer sheetCount = ((totalRowCount % ExcelConstant.PER_SHEET_ROW_COUNT == 0) ?(totalRowCount / ExcelConstant.PER_SHEET_ROW_COUNT) : (totalRowCount / ExcelConstant.PER_SHEET_ROW_COUNT + 1));// 根据总记录数创建sheet并分配标题for (int i = 0; i < sheetCount; i++) {SXSSFSheet sheet = wb.createSheet("sheet" + (i + 1));SXSSFRow headRow = sheet.createRow(i);for (int j = 0; j < titles.length; j++) {SXSSFCell headRowCell = headRow.createCell(j);headRowCell.setCellValue(titles[j]);}}return wb;}/*** 下载EXCEL到本地指定的文件夹** @param wb         EXCEL对象SXSSFWorkbook* @param exportPath 导出路径*/public static void downLoadExcelToLocalPath(SXSSFWorkbook wb, String exportPath) {FileOutputStream fops = null;try {fops = new FileOutputStream(exportPath);wb.write(fops);} catch (Exception e) {e.printStackTrace();} finally {if (null != wb) {try {wb.dispose();} catch (Exception e) {e.printStackTrace();}}if (null != fops) {try {fops.close();} catch (Exception e) {e.printStackTrace();}}}}/*** 下载EXCEL到浏览器** @param wb       EXCEL对象XSSFWorkbook* @param response* @param fileName 文件名称* @throws IOException*/public static void downLoadExcelToWebsite(SXSSFWorkbook wb, HttpServletResponse response, String fileName) throws IOException {response.setHeader("Content-disposition", "attachment; filename="+fileName);//设置下载的文件名OutputStream outputStream = null;try {outputStream = response.getOutputStream();wb.write(outputStream);} catch (Exception e) {e.printStackTrace();} finally {if (null != wb) {try {wb.dispose();} catch (Exception e) {e.printStackTrace();}}if (null != outputStream) {try {outputStream.close();} catch (Exception e) {e.printStackTrace();}}}}/*** 导出Excel到本地指定路径** @param totalRowCount           总记录数* @param titles                  标题* @param exportPath              导出路径* @param writeExcelDataDelegated 向EXCEL写数据/处理格式的委托类 自行实现* @throws Exception*/public static final void exportExcelToLocalPath(Integer totalRowCount, String[] titles, String exportPath, WriteExcelDataDelegated writeExcelDataDelegated) throws Exception {logger.info("开始导出:" + DateUtil.formatDate(new Date(), DateUtil.YYYY_MM_DD_HH_MM_SS));// 初始化EXCELSXSSFWorkbook wb = PoiUtil.initExcel(totalRowCount, titles);// 调用委托类分批写数据int sheetCount = wb.getNumberOfSheets();for (int i = 0; i < sheetCount; i++) {SXSSFSheet eachSheet = wb.getSheetAt(i);for (int j = 1; j <= ExcelConstant.PER_SHEET_WRITE_COUNT; j++) {int currentPage = i * ExcelConstant.PER_SHEET_WRITE_COUNT + j;int pageSize = ExcelConstant.PER_WRITE_ROW_COUNT;int startRowCount = (j - 1) * ExcelConstant.PER_WRITE_ROW_COUNT + 1;int endRowCount = startRowCount + pageSize - 1;writeExcelDataDelegated.writeExcelData(eachSheet, startRowCount, endRowCount, currentPage, pageSize);}}// 下载EXCELPoiUtil.downLoadExcelToLocalPath(wb, exportPath);logger.info("导出完成:" + DateUtil.formatDate(new Date(), DateUtil.YYYY_MM_DD_HH_MM_SS));}/*** 导出Excel到浏览器** @param response* @param totalRowCount           总记录数* @param fileName                文件名称* @param titles                  标题* @param writeExcelDataDelegated 向EXCEL写数据/处理格式的委托类 自行实现* @throws Exception*/public static final void exportExcelToWebsite(HttpServletResponse response, Integer totalRowCount, String fileName, String[] titles, WriteExcelDataDelegated writeExcelDataDelegated) throws Exception {logger.info("开始导出:" + DateUtil.formatDate(new Date(), DateUtil.YYYY_MM_DD_HH_MM_SS));// 初始化EXCELSXSSFWorkbook wb = PoiUtil.initExcel(totalRowCount, titles);// 调用委托类分批写数据int sheetCount = wb.getNumberOfSheets();for (int i = 0; i < sheetCount; i++) {SXSSFSheet eachSheet = wb.getSheetAt(i);for (int j = 1; j <= ExcelConstant.PER_SHEET_WRITE_COUNT; j++) {//查询开始页int currentPage = i * ExcelConstant.PER_SHEET_WRITE_COUNT + j;//分批查绚数据量int pageSize = ExcelConstant.PER_WRITE_ROW_COUNT;//开始行int startRowCount = (j - 1) * ExcelConstant.PER_WRITE_ROW_COUNT + 1;//结束行int endRowCount = startRowCount + pageSize - 1;writeExcelDataDelegated.writeExcelData(eachSheet, startRowCount, endRowCount, currentPage, pageSize);}}// 下载EXCELPoiUtil.downLoadExcelToWebsite(wb, response, fileName);logger.info("导出完成:" + DateUtil.formatDate(new Date(), DateUtil.YYYY_MM_DD_HH_MM_SS));}
}

编写DEMO
serviceimpl

 public ResultVO export(HttpServletResponse response) {//获取数据总条数Integer totalRowCount = excelRepository.findTotal();String filaName = "用户EXCEL.xlsx";//对应excel表体列字段String[] tital = new String[]{"用户id","姓名"};try {PoiUtil.exportExcelToWebsite(response, totalRowCount, filaName, tital, new WriteExcelDataDelegated() {@Overridepublic void writeExcelData(SXSSFSheet eachSheet, Integer startRowCount, Integer endRowCount, Integer currentPage, Integer pageSize) throws Exception {//此处进行分批次查询,List<ExcelModel> list = excelRepository.findExcelModelList(currentPage,pageSize);if (!list.isEmpty()) {for (int i = startRowCount; i <= endRowCount; i++) {SXSSFRow eachDataRow = eachSheet.createRow(i);if ((i - startRowCount) < list.size()) {ExcelModel model = list.get(i - startRowCount);eachDataRow.createCell(0).setCellValue(model.getId());eachDataRow.createCell(1).setCellValue(model.getPath());}}}}});return new ResultVO();} catch (Exception e) {e.printStackTrace();}return null;}

测试
经测试100W数据量导出时间73s,后续可进行再优化

实现方式二
直接通过新版的SXSSFWorkbook来解决大数据量导出问题,此方式
简明,但是不适用于过大数据量

  public ResultVO export2(HttpServletResponse response) {try {// 在内存当中保持 100 行 , 超过的数据放到硬盘中在内存当中保持 100 行 , 超过的数据放到硬盘中SXSSFWorkbook workbook = new SXSSFWorkbook(100);workbook.createSheet("sheet1");SXSSFSheet rows = workbook.getSheetAt(0);List<ExcelModel> list = excelRepository.finds();for (int i = 0; i < list.size(); i++) {rows.createRow(i);rows.getRow(i).createCell(0).setCellValue(list.get(i).getId());rows.getRow(i).createCell(1).setCellValue(list.get(i).getPath());}// 打开目的输入流,不存在则会创建OutputStream outputStream = null;// 打开目的输入流,不存在则会创建response.setHeader("Content-disposition","attachment; filename= aa.xlsx");workbook.write(response.getOutputStream());outputStream.close();} catch (Exception e) {e.printStackTrace();}return null;}

实现方式三
采用阿里开源的(EasyExcel)来实现百万级数据量导出,具体可参考链接
https://blog.csdn.net/qq_35206261/article/details/88579151

java百万级大数据量导出相关推荐

  1. POI百万级大数据量EXCEL导出

    一. 简介 excel导出,如果数据量在百万级,会出现俩点内存溢出的问题: 1. 查询数据量过大,导致内存溢出. 该问题可以通过分批查询来解决: 2. 最后下载的时候大EXCEL转换的输出流内存溢出: ...

  2. Java实现excel大数据量导出

    1.pom.xml配置依赖包 <dependency><groupId>org.apache.poi</groupId><artifactId>poi& ...

  3. java大数据量导出csv文件并压缩

    java大数据量导出csv文件并压缩 java使用POI大数据量导出excel一般会存在以下几个问题: 一次从数据库查询出这么大数据,查询缓慢 查询数据量过大时会内存溢出 解决方案:分页查询数据,比如 ...

  4. php导出1万条数据excel_实用!用PHP导出百万级大数据到Excel

    实用!用PHP导出百万级大数据到Excel 作者:PHPYuan 时间:2018-07-31 03:41:41 关注我们的人 月薪都过万了 一.数据量分析 假设我们需要单日导入的数量为20W+ Exc ...

  5. 大数据导出excel大小限制_大数据量导出Excel的方案

    测试共同条件: 数据总数为110011条,每条数据条数为19个字段. 电脑配置为:P4 2.67GHz,1G内存. 一.POI.JXL.FastExcel比较 POI.JXL.FastExcel均为j ...

  6. 高性能 + 百万级excel数据导入导出

    高性能 + 百万级excel数据导入导出 内容介绍 github地址 使用文档地址 安装注意事项 仅以mac举例 ide中提示相关代码 下载demo 内容介绍 [转载]https://learnku. ...

  7. EasyExcel 分Sheet实现大数据量导出

    EasyExcel 分 Sheet 实现大数据量导出 [场景]平台用户导出数据量达 w 级别的数据时界面白屏或按钮无响应. [解决方案]做异步导出,用户触发点击时创建导出消息并开启单独线程处理导出,处 ...

  8. 大数据导出excel大小限制_EXCEL大数据量导出的解决方案

    将web页面上显示的报表导出到excel文件里是一种很常见的需求.润乾报表的类excel模型,支持excel文件数据无失真的导入导出,使用起来非常的方便.然而,当数据量较大的情况下,excel本身的支 ...

  9. 数据库查询经常卡死?面对亿级大数据量,我是这么展示分析的

    建议你们看到文末,不会亏待你们 日常一提数据分析和可视化,就想到这个工具操作要多简单易用,图表要多美多炫,然而总是忽略背后的数据支撑. excel 几十万行数据就卡死崩,谈何数据透视表.可视化? 近千 ...

最新文章

  1. Insertion Loss Return Loss
  2. Android布局之相对布局——RelativeLayout
  3. Oracle导出表(即DMP文件)的两种方法
  4. 十分简洁的手机浏览器 lydiabox
  5. 李小璐PGONE事件对推荐系统的考验
  6. LLS战队高级软件工程第九次作业敏捷冲刺七
  7. 使用C#开发交互式命令行应用
  8. win10更新失败,怎么删除临时文件下面的更新
  9. eclipse maven项目 class类部署不到tomcat下_Springboot介绍以及用Eclipse搭建一个简单的Springboot项目教程
  10. SAP License:新总账
  11. Xamarin.Android AlertDialog中的EditText打上去字为什么不显示?也没有光标闪烁
  12. access导入失败:操作必须使用一个可更新的查询。
  13. 家庭WIFI路由器当交换机用
  14. 计算机无法安装cad怎么办,电脑安装AutoCAD提示未正常卸载无法安装如何解决
  15. 微信小程序PNG图片去白底
  16. 网站建设中百度快照劫持是什么?劫持百度快照是怎么回事?
  17. 基于C#的图片浏览及显示功能(源码)
  18. IOS 蓝牙相关-BabyBluetooth蓝牙库介绍(4)
  19. Cython 图片去除水印
  20. PHP学习----换行符

热门文章

  1. 美国研究生院计算机数据科学排名,美国研究生数据科学专业排名
  2. 脱离文档流和恢复文档流的方法
  3. firefox插件(plugin)开发概述
  4. 四大机器学习降维方法
  5. 数字化的一切都会在安全沙箱里面
  6. Java微服务面试题整理
  7. 安卓四大组件(小白篇)
  8. 在Unity3d项目中利用Udp进行局域网内通信
  9. 7-36 大炮打蚊子(15 分)
  10. Qt向MySQL中插入图片