基于EasyExcel封装的Excel工具类,支持高效导出和读取操作

server/2025/2/28 17:46:07/

以下是一个基于EasyExcel封装的Excel工具类,支持高效导出和读取操作:

java">import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import org.apache.poi.ss.usermodel.HorizontalAlignment;import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import java.util.Map;/*** Excel工具类(基于EasyExcel)*/
public class ExcelUtil {/*** 导出Excel到输出流(单个Sheet)* @param outputStream 输出流* @param dataList 数据列表* @param templateClass 数据模型类* @param sheetName 工作表名称* @param <T> 数据类型*/public static <T> void exportToStream(OutputStream outputStream, List<T> dataList,Class<T> templateClass,String sheetName) {ExcelWriter excelWriter = null;try {// 设置表头样式WriteCellStyle headStyle = new WriteCellStyle();headStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);// 设置正文样式WriteCellStyle contentStyle = new WriteCellStyle();contentStyle.setHorizontalAlignment(HorizontalAlignment.LEFT);// 构建样式策略HorizontalCellStyleStrategy styleStrategy = new HorizontalCellStyleStrategy(headStyle, contentStyle);excelWriter = EasyExcel.write(outputStream, templateClass).registerWriteHandler(styleStrategy).build();WriteSheet writeSheet = EasyExcel.writerSheet(sheetName).build();excelWriter.write(dataList, writeSheet);} finally {if (excelWriter != null) {excelWriter.finish();}}}/*** 导出大数据量Excel(分Sheet写入)* @param outputStream 输出流* @param dataSupplier 数据提供函数(分页查询)* @param templateClass 数据模型类* @param sheetSize 每个Sheet最大行数* @param <T> 数据类型*/public static <T> void exportBigData(OutputStream outputStream,PageDataSupplier<T> dataSupplier,Class<T> templateClass,int sheetSize) {ExcelWriter excelWriter = null;try {excelWriter = EasyExcel.write(outputStream, templateClass).build();int sheetIndex = 1;int page = 1;List<T> dataChunk;while (!(dataChunk = dataSupplier.getPageData(page, sheetSize)).isEmpty()) {WriteSheet writeSheet = EasyExcel.writerSheet("Sheet" + sheetIndex).build();excelWriter.write(dataChunk, writeSheet);if (page % (sheetSize / 5000) == 0) { // 每5000行一个SheetsheetIndex++;}page++;}} finally {if (excelWriter != null) {excelWriter.finish();}}}/*** 从输入流读取Excel数据* @param inputStream 输入流* @param templateClass 数据模型类* @param dataConsumer 数据消费接口* @param <T> 数据类型*/public static <T> void readFromStream(InputStream inputStream,Class<T> templateClass,DataConsumer<T> dataConsumer) {EasyExcel.read(inputStream, templateClass, new AnalysisEventListener<T>() {private static final int BATCH_SIZE = 2000;private List<T> cachedList = new ArrayList<>(BATCH_SIZE);@Overridepublic void invoke(T data, AnalysisContext context) {cachedList.add(data);if (cachedList.size() >= BATCH_SIZE) {dataConsumer.consume(cachedList);cachedList = new ArrayList<>(BATCH_SIZE);}}@Overridepublic void doAfterAllAnalysed(AnalysisContext context) {if (!cachedList.isEmpty()) {dataConsumer.consume(cachedList);}}}).sheet().doRead();}/*** 导出Web响应(自动设置Header)* @param response HttpServletResponse* @param fileName 文件名(不带后缀)* @param dataList 数据列表* @param templateClass 数据模型类* @param <T> 数据类型*/public static <T> void exportWebResponse(HttpServletResponse response,String fileName,List<T> dataList,Class<T> templateClass) throws IOException {response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");response.setCharacterEncoding("utf-8");String encodedFileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + encodedFileName + ".xlsx");exportToStream(response.getOutputStream(), dataList, templateClass, "Sheet1");}// 自定义转换器注册public static void registerConverter(Converter<?> converter) {EasyExcel.registerConverter(converter);}/*** 分页数据提供接口*/public interface PageDataSupplier<T> {List<T> getPageData(int pageNumber, int pageSize);}/*** 数据消费接口*/public interface DataConsumer<T> {void consume(List<T> dataBatch);}// 示例数据模型public static class UserExportModel {@ExcelProperty("用户ID")private Long userId;@ExcelProperty(value = "用户状态", converter = StatusConverter.class)private Integer status;// 其他字段...}// 示例转换器public static class StatusConverter implements Converter<Integer> {private static final Map<Integer, String> STATUS_MAP = Map.of(1, "正常",2, "冻结",3, "注销");@Overridepublic String convertToExcelData(Integer value, ExcelContentProperty property, GlobalConfiguration globalConfig) {return STATUS_MAP.getOrDefault(value, "未知状态");}}
}

使用示例

1. 简单导出到文件:

java">try (FileOutputStream fos = new FileOutputStream("export.xlsx")) {List<User> userList = userService.getAllUsers();ExcelUtil.exportToStream(fos, userList, User.class, "用户数据");
}

2. Web响应导出:

java">@GetMapping("/export")
public void exportUsers(HttpServletResponse response) throws IOException {List<User> userList = userService.getAllUsers();ExcelUtil.exportWebResponse(response, "用户列表", userList, User.class);
}

3. 大数据量分页导出:

java">ExcelUtil.exportBigData(response.getOutputStream(), (page, size) -> userService.getUsersByPage(page, size),User.class,100_0000); // 每个Sheet存储100万数据

4. 读取Excel数据:

java">InputStream inputStream = new FileInputStream("import.xlsx");
ExcelUtil.readFromStream(inputStream, UserImportModel.class, batch -> {userService.batchProcess(batch);
});

主要特性说明

  1. 高性能导出:

    • 使用分Sheet写入策略,每个Sheet最多存储指定行数
    • 自动注册样式策略(表头居中,内容左对齐)
    • 支持流式导出,避免内存溢出
  2. 灵活读取:

    • 批量处理机制(默认每2000条处理一次)
    • 自动内存管理,适合大文件读取
  3. 扩展功能:

    • 支持自定义转换器(通过registerConverter方法)
    • 支持Web响应自动配置(自动设置Content-Type和文件名)
    • 提供分页数据获取接口,适合数据库分页查询
  4. 内存安全:

    • 导出时自动分批次写入
    • 读取时使用事件驱动模型,不保留完整数据在内存
    • 默认开启SXSSF模式(流式写入)
  5. 样式配置:

    • 预定义表头和内容样式
    • 可通过覆盖registerWriteHandler方法自定义样式

最佳实践建议

  1. 大文件导出:

    • 使用exportBigData方法配合分页查询
    • 设置合适的Sheet大小(推荐50-100万行/Sheet)
    • 添加内存监控逻辑
  2. 数据转换:

    • 为枚举字段创建专用转换器
    • 对日期字段使用@ExcelProperty#format
    • 复杂转换建议使用数据库联查
  3. 异常处理:

    • 在读取时添加数据校验逻辑
    • 使用全局异常处理器捕获ExcelAnalysisException
  4. 性能优化:

    • 导出时关闭自动列宽计算(.autoTrim(false)
    • 读取时设置合适的缓存大小(ReadCache
    • 避免在循环中创建大量临时对象

http://www.ppmy.cn/server/171344.html

相关文章

Vue.js 使用 snapshot 测试组件

Vue.js 使用 snapshot 测试组件 今天我们来聊聊如何使用 snapshot 测试组件&#xff0c;这是一种非常有效的方式来确保组件的输出没有意外的变化。Snapshot 测试可以捕获组件渲染的 HTML 结构&#xff0c;并在后续测试中与之前保存的快照进行比对&#xff0c;如果快照发生了变…

PyCharm 的使用 + PyCharm快捷键 + 切换中文界面

2025 - 02 - 27 - 第 62 篇 Author: 郑龙浩 / 仟濹 【PyCharm的使用】 文章目录 如何使用Pycharm1 新建工程&#xff0c;新建 .py 文件&#xff0c;运行2 常用快捷键3 其他快捷键 - DeepSeek 总结如下**代码编辑****导航与定位****查找与替换****运行与调试****代码重构****其…

几个api

几个api 原型链 可以阅读此文 Function instanceof Object // true Object instanceof Function // true Object.prototype.isPrototypeOf(Function) // true Function.prototype.isPrototypeOf(Object) // true Object.__proto__ Function.prototype // true Function.pro…

什么是GPU

GPU&#xff08;Graphics Processing Unit&#xff0c;图形处理器&#xff09;是一种专门设计用于处理图形和并行计算的处理器。最初&#xff0c;GPU主要用于图形渲染&#xff0c;如游戏、3D建模和视频处理。然而&#xff0c;随着技术的发展&#xff0c;GPU在通用计算领域的应用…

Linux相关知识(文件系统、目录树、权限管理)和Shell相关知识(字符串、数组)

仅供自学&#xff0c;请去支持javaGuide原版书籍。 1.Linux 1.1.概述 Linux是一种类Unix系统。 严格来讲&#xff0c;Linux 这个词本身只表示 Linux内核&#xff0c;单独的 Linux 内核并不能成为一个可以正常工作的操作系统。所以&#xff0c;就有了各种 Linux 发行版&#…

Ubuntu 下 nginx-1.24.0 源码分析 - ngx_init_cycle 函数 - 详解(3)

详解&#xff08;3&#xff09; 初始化路径数组&#xff08;paths&#xff09; n old_cycle->paths.nelts ? old_cycle->paths.nelts : 10;if (ngx_array_init(&cycle->paths, pool, n, sizeof(ngx_path_t *))! NGX_OK){ngx_destroy_pool(pool);return NULL;}ng…

TCP的三次握手与四次挥手:建立与终止连接的关键步骤

引言 ‌TCP&#xff08;传输控制协议&#xff09;工作在OSI模型的传输层‌。OSI模型将计算机网络功能划分为七个层级&#xff0c;从底层到顶层依次是&#xff1a;物理层、数据链路层、网络层、传输层、会话层、表示层和应用层。传输层负责在网络节点之间提供可靠的端到端通信&a…

Spring Boot(七):Swagger 接口文档

1. Swagger 简介 1.1 Swagger 是什么&#xff1f; Swagger 是一款 RESTful 风格的接口文档在线自动生成 功能测试功能软件。Swagger 是一个规范和完整的框架&#xff0c;用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。目标是使客户端和文件系统作为服务器以同样的…