EasyExcel 分 Sheet 实现大数据量导出

【场景】平台用户导出数据量达 w 级别的数据时界面白屏或按钮无响应。

【解决方案】做异步导出,用户触发点击时创建导出消息并开启单独线程处理导出,处理成功以后将文件下载路径更新到消息中心提醒用户下载。

【注意的问题】As we all knew, everything has its limits,Excel 也是(03 版本 Excel 单张 Sheet 容量是 65536 行 ,07 版本单张 Sheet 容量是 1048575 行),超过了容量则 api 报错。invalid row number (1048576) outside allowable range (0..1048575)

【解决办法】分 Sheet 导出,如果导出的数据量超过单一张 Sheet 的容量就创建一个新 Sheet 进行存放

实现步骤如下:

引入依赖

<dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>2.2.10</version>
</dependency>

具体方式(参考代码)

/**
 * 异步导出学生数据
 * @param param 查询参数
 * @param loginUserId 当前登录人ID
 * @param classId 班级ID
 * @return
 */
public Result exportStudentInfoBigData(StudentInfoExportQueryParam  param, Integer loginUserId, Integer classId) {try {String title = "学生信息报表"; // 表格顶部标题String fileName = new String((title + String.format("%tF", new Date()) + "-导出").getBytes(), StandardCharsets.UTF_8);// 创建任务String messageTitle = title + "导出正在处理中(可能时间比较长,请及时关注消息处理进度)...";MessageCenter downloadMsg = new MessageCenter(loginUserId, Constants.COMMON_NUMBER_1, messageTitle, null);messageCenterMapper.insert(downloadMsg);// 输出到临时目录String savePath = "";if (Arrays.asList("prod".split(",")).contains(appConfiguration.getActive())) { // 环境配置临时文件位置savePath = "/mnt/export/";} else {savePath = "D:/export/";}String fileStr = fileName + ".xlsx";String saveFileName = savePath + fileStr;// 创建临时文件File savefile = new File(savePath);if (!savefile.exists()) {savefile.mkdirs();}FileOutputStream fos = new FileOutputStream(saveFileName);// 异步方式String bodyFormat = "您于" + DateUtils.dateTimeNow(DateUtils.YYYY_MM_DD_HH_MM_SS) + "提交导出的【%s】已完成, 下载地址: <a style='color: blue' href='%s' download='%s'>点击下载</a>";// 校验是否已经存在导出中的数据Map<String, Object> paraMap = new HashMap<String, Object>();paraMap.put("userId", loginUserId);paraMap.put("messageType", 1);paraMap.put("messageTitle", title);paraMap.put("status", Constants.COMMON_NUMBER_0); // 未读List<MessageCenter> downloadMsgList = messageCenterMapper.findListByParam(paraMap);if (!CollectionUtils.isEmpty(downloadMsgList)) {throw new CustomException("您已有正在处理中的下载任务,请到消息中查看状态完成后下载文件");}param.setUserClassId(classId);param.setLoginUserId(loginUserId);// 计算总数分片导出// 获取最大IDparam.setQueryType(1);Integer maxId = studentInfoMapper.getMaxOrMinId(param);// 获取最小IDparam.setQueryType(2);Integer minId = studentInfoMapper.getMaxOrMinId(param);// 查询此时最小ID~最大ID之间的总数param.setMaxId(maxId);param.setMinId(minId);// 根据当前条件查询到的总数据量Long totalCount = studentInfoMapper.selectCountStudentInfoExcel(param);log.info("当前导出总数量:{}", totalCount);ExcelWriter excelWriter = null;if(totalCount > Constants.EXCEL_SHEET_CAPACITY) { // 如果总数超出单个sheet的大小 1048575excelWriter = EasyExcel.write(fos, StudentInfoExportExcel.class).build();// 计算Sheet个数: 如果超过单个sheet大小,需要分sheet,否则报错: invalid row number (1048576) outside allowable range (0..1048575)Integer pageSize = Constants.EXPORT_MAX_SHEET_PAGE_SIZE; // 设置的单个Sheet的数据量,必须小于1048576,在此设置Constants.EXPORT_MAX_SHEET_PAGE_SIZE=100,0000long sheetCount = (totalCount + Constants.EXPORT_MAX_SHEET_PAGE_SIZE - 1) / pageSize;for (Integer pageNum = 1; pageNum <= sheetCount; ++pageNum) {WriteSheet writeSheet = EasyExcel.writerSheet(pageNum, fileName + "_Sheet表_" + pageNum).head(StudentInfoExportExcel.class).build();PageHelper.startPage(pageNum, pageSize, false); // 分页不统计List<StudentInfoExportExcel> excelList = studentInfoMapper.findAndExportStudentInfoList(param);excelWriter.write(excelList, writeSheet);excelList.clear(); // 导出当前列表以后清空,释放服务器内存}}else { // 不超出则一次性全部导出excelWriter = EasyExcel.write(fos, StudentInfoExportExcel.class).build();WriteSheet writeSheet = EasyExcel.writerSheet(fileName).head(StudentInfoExportExcel.class).build();List<StudentInfoExportExcel> excelList = studentInfoMapper.findAndExportStudentInfoList(param);excelWriter.write(excelList , writeSheet);excelList.clear(); // 导出当前列表以后清空,释放服务器内存}// 关闭excelWriter.finish();byte[] fileByte = FileUtils.readFileToBinary(saveFileName); // 将临时文件转为二进制数组,然后上传到云存储服务器if (Objects.nonNull(fileByte) && fileByte.length > 0) {InputStream is = new ByteArrayInputStream(fileByte);String url = cloudStorageService.uploadFile(new MockMultipartFile(fileName, fileStr, "", is));log.info("导出出来完成,数据量:{}, 路径:{}", totalCount, url);messageTitle = title + "导出处理完成";downloadMsg.setMessageTitle(messageTitle);downloadMsg.setMessageBody(String.format(bodyFormat, fileName, url, fileName));} else {messageTitle = title + "导出处理失败";downloadMsg.setMessageTitle(messageTitle);downloadMsg.setMessageBody(title + "导出处理失败,请重新点击导出");}downloadMsg.setStatus(0);messageCenterMapper.updateById(downloadMsg);savefile.delete();// 关闭流fos.close();return Result.success("导出已经处理正在处理中,请到消息中心查看处理结果并下载文件");} catch (Exception e) {log.info("学生信息信息导出异常:{}", e);return Result.error("学生信息导出系统异常");}
}

【总结】

1. 创建代办消息,提示告诉用户要耐心等待处理完成;与此通过线程池启动线程处理导出。

2. 处理过程:

(1)根据当前用户点击导出时的查询条件,获取当前目标导出数据的最小 ID 和最大 ID;

(2)再统计最小 ID~ 最大 ID 范围内所有的数据总数 totalCount(用于下一步计算要多少个 Sheet 来装)

(3)判断 totalCount 没有超过 Sheet 容量就一次性导出,如果超过则分 Sheet

(4)根据数据总数 totalCount 计算 Sheet 张数,每张 Sheet 存储 100w 条数据;

Integer pageSize = Constants.EXPORT_MAX_SHEET_PAGE_SIZE; // 设置的单个 Sheet 的数据量,必须小于 1048576,在此设置(Constants.EXPORT_MAX_SHEET_PAGE_SIZE=100,0000)

long sheetCount = (totalCount + Constants.EXPORT_MAX_SHEET_PAGE_SIZE - 1) / pageSize;

(5)循环 Sheet 数量进行分页查询和加载到 Excel 中,加载每一页后要清空 List,否则服务器内存容易会被消耗完而导致服务器崩溃重启。

(6)将生成 Excel 存储到 Linux / 本地服务器空间比较大的文件路径,将其读取并上传到云存储,上传完成后得到下载 url,删除 Linux / 本地服务器文件释放磁盘空间。

(7)更新消息中心信息,把 url 放入消息体,提示用户当前的导出处理完成,可以点击链接下载。

最近在工作中遇到用户反馈说导出功能不好用,就单独抽出时间来优化平台中导出的功能,之前把导出优化的任务分给同事来搞,写的代码看起来就重写,导出的数据都不对,最后还是要我出马。

积硅步以致千里

--------------------------------------------------------------------->>[Every problem has its solutions]

EasyExcel 分Sheet实现大数据量导出相关推荐

  1. java百万级大数据量导出

    java实现百万级大数据量导出 实现方式一 一,分析 excel导出如果数据量过大,会出现两个内存溢出的问题 查绚数据量过大,导致内存溢出(可通过分批查绚解决) 下载的时候大EXCEL转换的输出流内存 ...

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

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

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

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

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

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

  5. 使用bcp进行大数据量导出导入

    SQL Server的导出导入方式有:在SQL Server中提供了导入导出的界面操作. --还没有解决的问题  --1:使用BCP命令时如果导出的字段为空想替换为其它描述(比如替换成NULL)如何办 ...

  6. 使用EasyExcel实现excel导出,支持百万大数据量导出-----超简单

    通过设置sheet数量,完成分批导出,每个sheet存100万数据,每次查询插入20万数据,避免超时,内存溢出等问题,可以根据服务器配置调整参数设置. 1.引入依赖 <dependency> ...

  7. 大数据量导出Excel ---待测试

    set ANSI_NULLS ON set QUOTED_IDENTIFIER ON go /*--数据导出EXCEL 导出查询中的数据到Excel,包含字段名,文件为真正的Excel文件 如果文件不 ...

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

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

  9. java使用poi实现大数据量导出为EXCEL

    总体的实现思想为: 每次查询出2w数据,并写入到临时文件 然后把这些文件写入到一个EXCEL里边,或者把这些文件压缩为zip文件,然后把Zip文件提供给下载(这里使用zip打包是因为,在Linux上也 ...

最新文章

  1. 【青少年编程(第33周)】Scratch(三级)公益活动开营了!
  2. 用简单的C语言实现多任务轮流切换(模拟操作系统线程机制)【转】
  3. tp点一共有多少_致命女人更新时间 致命女人第一季一共多少集在线观看地址
  4. 为什么短视频难逃【单列沉浸式】的真香定律?
  5. 解构华为云HE2E项目中的容器技术应用
  6. 电商场景下,如何处理消费过程中的重复消息?
  7. win10电脑中病毒了怎么办,如何解决电脑中病毒
  8. 二分排序(java)
  9. VC6 SDK 下载
  10. 操作系统——四种进程调度算法模拟实现(C语言)
  11. Chapter6 机器人系统仿真(Ⅰ)---使用rviz集成urdf、xacro建造机器人模型
  12. Java工程师成神之路(2020最新版)
  13. js鼠标点击位置 弹出层由中心向四周缓慢扩大
  14. 华硕路由 ac ax_【路由器】华硕网络开启802.11ax电竞生态
  15. 大一期末项目:学生管理系统
  16. 21世纪七大数学难题
  17. Photoshop鼠绘教程:逼真鲜活金鱼的绘制
  18. Java语言与C语言的区别
  19. 正大国际:做外盘期货主帐户有什么风险?
  20. 微信小程序iOS视频播放开始黑屏问题

热门文章

  1. PCB线宽、过孔、载流能力整理
  2. 快递老大顺丰,首次披露自己的技术“护城河”
  3. Total Control 远程控制手机软件 和 Vysor Chrome 的插件实时同步投影
  4. 受挤德尔福破产 通用北美大肆下单中国
  5. 隐形AR眼镜厂商Mojo Vision裁员75%,专注Micro LED技术
  6. [docker]四、docker必会的常用命令和用docker起MySQL容器需要注意的事项
  7. 工程伦理-美国密歇根州大坝溃决事件的分析与思考
  8. 数字源表用于纳米发电测试
  9. 人工智能之父、Lisp语言发明者约翰麦卡锡去世,又一巨人去了
  10. 在express路由中使用ejs模板引擎