easy-excel批量导出数据

2023-11-14

easy-excel批量导出

前言

最近遇到个用户数据批量导出excel的需求,第一次看到这个需求大家第一时间想到的应该大多都是easy-excel这个框架吧,哈哈,我第一时间想到的也是这个框架。
但是对于少量的数据,比如有10个用户这样的数据肯定没有啥太大的问题,但是对于百万级数据可能就会有问题,总不能一次性吧100w数据从数据库里面查出来吧,这无疑是sql炼狱,同时也面临着oom的风险,面对一个线上服务这显然是不理智的。

解决思路

面对如上情况大多数情况的解决思路都是分批量查询数据,比如100w数据每次查询10w或者1w分多次查询出来,以时间换空间。面对如此情况easy-excel版本可能支持的不是很好,那么下面就提供一下该场景的解决方案。

实际解决

实际在解决该需求时还要考虑到excel的最大可承载数据量为 1048576行(该数据由百度查询得到,也可自行下拉excel表格到最底端查看),但是如果数据超过这些行数怎么办,直接报错?显然这对于一个功能来说是不可取的,这时可以设定一个边界值来指定超过该边界值后导出多个excel,但是把多个excel直接导出到服务器上,把地址返回给前端,让前端拿着地址去web服务器上面下载,也不是一个可取的方法。但是如何能让多个excel作为一个文件导出呢?方案到了这大家可能都会想到了,那就是.zip这种压缩文件的形式。现在有了思路了,那么说干就干,下面我们直接来看一下代码。

/**
 * 导出excel工具
 * @author wangshaoyu
 * @date 2022/11/20
 */
public class ExcelUtils {
	/**
	 * 导出多个sheet到多个excel文件,并压缩到一个zip文件
	 * @param zipFilename 下载时压缩包名称
	 * @param response 请求返回流
	 * @param header excel实体类类型
	 * @param splitTimes 次数分界,excel数据导入多少次后创建下一个excel文件
	 * @param supplier 供给侧函数式接口,提供get方法,该方法可自定义
	 * @return void   
	 */
	public static <T> void exportZip(String zipFilename, HttpServletResponse response, Class<T> header, int splitTimes, Supplier<List<T>> supplier) {
		if (zipFilename == null || zipFilename.isEmpty()) {
			zipFilename = "export";
		} else if (zipFilename.toLowerCase(Locale.ROOT).endsWith(".zip")) {
			zipFilename = zipFilename.substring(0, zipFilename.length() - 3);
		}
		try {
			// 这里URLEncoder.encode可以防止中文乱码
			String downFileName = URLEncoder.encode(zipFilename, "utf-8");
			response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + downFileName + ".zip");
			response.setContentType("application/x-msdownload");
			response.setCharacterEncoding("utf-8");
			//开始存入
			try (ZipOutputStream zipOut = new ZipOutputStream(response.getOutputStream())) {
				List<T> exportData = null;
				int count = 0;
				int fileIndex = 1;
				ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
				ExcelWriterBuilder builder = EasyExcel.write(outputStream).autoCloseStream(false)
					.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy());
				ExcelWriter excelWriter = builder.build();
				zipOut.putNextEntry(new ZipEntry(String.format("%s-%d.xls", zipFilename, fileIndex)));
				WriteSheet writeSheet = EasyExcel.writerSheet(zipFilename).head(header).build();
				exportData = supplier.get();
				if (Func.isEmpty(exportData)) {
					throw new ServiceException("导出数据为空");
				}
				while (Func.isNotEmpty(exportData)) {
					count += 1;
					excelWriter.write(exportData, writeSheet);
					exportData = supplier.get();
					//如果到了分割点,则证明该excel文件已经到达存储临界值,直接保存该文件
					if (count % splitTimes == 0) {
						fileIndex += 1;
						excelWriter.finish();
						outputStream.writeTo(zipOut);
						zipOut.closeEntry();
						//如果导出数据不为空则证明存在下一个excel文件,则在压缩包中新增一个excel文件
						if (Func.isNotEmpty(exportData)) {
							outputStream = new ByteArrayOutputStream();
							builder = EasyExcel.write(outputStream).autoCloseStream(false)
								// 自动适配
								.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy());
							excelWriter = builder.build();
							zipOut.putNextEntry(new ZipEntry(String.format("%s-%d.xls", zipFilename, fileIndex)));
							writeSheet = EasyExcel.writerSheet(zipFilename).head(header).build();
						}
					}
				}

				//count % splitTimes == 0 代表本次导出刚好导出整数个文件,且每个文件中都有规定大小的数据
				if (count % splitTimes == 0) {
					return;
				}
				excelWriter.finish();
				outputStream.writeTo(zipOut);
				zipOut.closeEntry();
			} catch (IOException e) {
				throw new RuntimeException("导出Excel异常", e);
			}
		} catch (UnsupportedEncodingException e) {
			throw new RuntimeException("导出excel异常");
		}
	}

	/**
	 * 导出单个excel文件,适用于大量数据但是未超过excel最大限制的情况
	 * @param fileName 文件名称
	 * @param response 请求返回数据流
	 * @param header excel实体类类型
	 * @param supplier 供给侧函数
	 * @return void   
	 * */
	public static <T> void exportExcel(String fileName, HttpServletResponse response, Class<T> header, Supplier<List<T>> supplier) {
		try {
			if (fileName == null || fileName.isEmpty()) {
				fileName = "export";
			} else if (fileName.toLowerCase(Locale.ROOT).endsWith(".xls")) {
				fileName = fileName.substring(0, fileName.length() - 3);
			} else if (fileName.toLowerCase(Locale.ROOT).endsWith(".xlsx")) {
				fileName = fileName.substring(0, fileName.length() - 4);
			}
			response.setContentType("application/vnd.ms-excel");
			response.setCharacterEncoding("utf-8");
			String excelName = URLEncoder.encode(fileName, "utf-8");
			response.setHeader("Content-disposition", "attachment;filename=" + excelName + ".xls");
			ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()).autoCloseStream(false)
				.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).build();
			WriteSheet writeSheet = EasyExcel.writerSheet(fileName).head(header).build();
			List<T> exportData = null;
			while (Func.isNotEmpty(exportData = supplier.get())) {
				excelWriter.write(exportData, writeSheet);
			}
			excelWriter.finish();
		} catch (Exception e) {
			throw new ServiceException("导出excel异常");
		}
	}
}

结语

对于工具类使用、代码bug、优化问题欢迎评论提出,作者会努力优化,耐心回答。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

easy-excel批量导出数据 的相关文章

随机推荐

  • Prometheus 监控mysql

    目录 下载安装mysqld exporter 在mysql中创建监控用户并赋权 启动mysqld exporter 添加到系统服务 浏览器访问服务器9104端口 在prometheus定义job来监控mysqld 运行prometheus并
  • Linux系统查看硬盘空间的常用命令!

    在Linux系统中 查看硬盘空间使用情况可以使用命令来完成 其中比较常见的命令有 df和du 那么它们具体如何使用呢 本文为大家详细介绍一下 快来学习吧 查看磁盘空间 df df命令以磁盘分区为单位查看文件系统中磁盘空间的使用情况 选项 h
  • VS2019 + Qt5.12 配置完成后,无法打开 Qt 源文件解决方案(非常实用)

    注 本文主要是解决 VS 无法打开 Qt 源文件问题 关于 VS Qt 配置问题 网上一搜一大堆 各个版本都有 这里就不做详细阐述了 最近自己在使用 VS2019 建立 Qt 工程的时候 遇到了无法打开 Qt 源文件问题 在网上阅读了大量的
  • 每天一个设计模式之 -- 组合模式

    组合模式 组合模式 Composite Pattern 又叫部分整体模式 是用于把一组相似的对象当作一个单一的对象 组合模式依据树形结构来组合对象 用来表示部分以及整体层次 这种类型的设计模式属于结构型模式 它创建了对象组的树形结构 这种模
  • 基于TCP的Socket网络编程

    前言 Socket通信是基于TCP IP协议的通信 在工作和做项目中应用非常广 下面来介绍下Socket网络编程 Socket的介绍 首先 在Socket网络编程中我们要了解两个重要的东西 ip和端口号 一台拥有IP地址的主机可以提供许多服
  • HttpContext在一般程序中无法引用

    刚刚写代码的时候 发现在类库的程序中无法直接使用HttpContext 经过学习发现 只要在引用上右击弹出下面界面 然后点击程序集 直接在搜索框中搜web 然后选用System Web 确定 再在你的cs文件中using System We
  • isolation forest在MATLAB和python上的简单应用

    isolation forest在MATLAB和python上的简单应用 1 问题描述 2 isolation forest 3 MATLAB实现isolation forest 4 python实现isolation fores 5 遇到
  • 【linux学习笔记】红帽Linux 7.8系统在虚拟机上的安装

    目录 一 打开VMware 选择新建虚拟机选项 二 选择自定义 并且点击下一步 三 可以选择默认继续下一步 可以进行更改虚拟机硬件兼容性 四 继续下一步 选择稍后安装系统 五 下一步选择客户操作系统Red Hat Enterprise Li
  • Centos8无法用yum下载

    原因 CentOS 8操作系统版本结束了生命周期 EOL Linux社区已不再维护该操作系统版本 解决方案 在服务器上执行如下代码 1 curl o etc yum repos d CentOS Base repo https mirror
  • 遍历Redis集群中的所有Key

    文章目录 1 背景s 2 需求 3 实现思路 4 SCAN 简介 5 用Jedis来实现Key扫描 5 1 基础的辅助工具类 5 2 批处理停止开关 5 3 扫描结果回调函数 5 4 实现单个Redis节点的Key扫描 5 5 实现Redi
  • 从软件的角度看待PCI和PCIE(二)

    PCI电路图 如图所示AD0 AD31 A表示地址 D表示数据 PCI是并行接口 怎么去分辨别当前使用的是地址还是数据呢 是根据FRAME这个引脚来分辨的 上面可以看出PCI是并行接口 在我们的印象中 并口传输的速率会比较高 但是当速率达到
  • mysql delete limit 使用方法详解

    一 mysql delete limit优点 用于DELETE的MySQL唯一的LIMIT row count选项用于告知服务器在控制命令被返回到客户端前被删除的行的最大值 本选项用于确保一个DELETE语句不会占用过多的时间 您可以只重复
  • php md化的nav,宝塔面板搭建onenav – 使用PHP开发的简约导航/书签管理系统

    onenav 使用PHP SQLite 3开发的简约导航 书签管理器 用宝塔搭建体验了一番 效果不错 这篇文章就来说说用宝塔搭建的体验教程 之前也介绍过Shiori书签管理程序 基于docker安装 喜欢的可以看看 1 功能特点支持后台管理
  • windows 只能同时 启动一个_如何将U盘一分为二(启动分区和存储分区两不误)...

    现在网上有很多关于U盘分区的帖子 有的方法过时不适用 有的过于深奥繁琐 难道将U盘一分为二 做成一个启动盘 文件存储盘有那么难吗 小编找来一篇有详实操作体验的U盘分区心得文章 希望能传达一个将U盘分成启动和存储两个分区的有效方法给广大用户们
  • ROS与navigation教程——ACML参数配置

  • 系统架构设计专业技能 · 信息系统基础

    系列文章目录 系统架构设计专业技能 网络技术 三 系统架构设计专业技能 系统安全分析与设计 四 系统架构设计师 系统架构设计高级技能 软件架构设计 一 系统架构设计师 系统架构设计高级技能 系统质量属性与架构评估 二 系统架构设计师 系统架
  • 一.初识Spring Boot ,了解什么是spring boot 及他的优点缺点应用背景

    目录 1 spring 能做什么 spring网站的提供的 2 为什么用SpringBoot 官网信息 3 1 SpringBoot优点 3 2 SpringBoot缺点 4 springboot 出现的时代背景 4 1 微服务 这是提出的
  • GPT专业应用:生成电影解说词

    正文共 969 字 阅读大约需要 3 分钟 内容创作者必备技巧 您将在3分钟后获得以下超能力 快速生成电影解说词 Beezy评级 B级 经过简单的寻找 大部分人能立刻掌握 主要节省时间 推荐人 Kim 编辑者 Linda 图片来源 Lexi
  • UI系统——作业与练习

    血条 Health Bar 的预制设计 具体要求如下 分别使用 IMGUI 和 UGUI 实现 使用 UGUI 血条是游戏对象的一个子元素 任何时候需要面对主摄像机 分析两种实现的优缺点 给出预制的使用方法 IMGUI制作血条 using
  • easy-excel批量导出数据

    easy excel批量导出 前言 最近遇到个用户数据批量导出excel的需求 第一次看到这个需求大家第一时间想到的应该大多都是easy excel这个框架吧 哈哈 我第一时间想到的也是这个框架 但是对于少量的数据 比如有10个用户这样的数