EasyExcel 快速上手指南:低内存解析 Excel 的 Java 工具箱(读取、写入与 Web 上传下载实战) 📅 发布时间:2026/9/19 14:52:39 👁 浏览次数: EasyExcel 快速上手指南低内存解析 Excel 的 Java 工具箱读取、写入与 Web 上传下载实战【免费下载链接】easyexcel快速、简洁、解决大文件内存溢出的java处理Excel工具项目地址: https://gitcode.com/gh_mirrors/ea/easyexcel导读EasyExcel 是阿里开源的一款 Java 解析与生成 Excel 的工具箱核心价值在于以极低的内存占用解析超大 Excel 文件。本指南围绕 EasyExcel 官方英文文档README_EN.md展开先解释它比 Apache POI、jxl 更省内存的底层原理与版本支持情况再以完整可运行的示例代码讲解读取 Excel、写入 Excel、Web 场景下的文件上传与下载三大核心用法并对照当前仓库中的源码easyexcel-core 模块与测试用例easyexcel-test 模块给出实现级佐证。读完本文你将能直接在自己的项目中引入 EasyExcel三行代码完成读、写与 Web 交互并理解为什么它可以放心处理大文件。为什么选择 EasyExcel针对内存溢出的重写方案在 Java 生态中解析与生成 Excel 的知名框架包括 Apache POI 与 jxl但它们都存在一个严重问题非常消耗内存。POI 提供了一套 SAX 模式的 API可以在一定程度上缓解内存溢出但仍有缺陷——例如对于 07 版xlsxExcel解压缩以及解压后的存储都是在内存中完成的内存消耗依然很大。EasyExcel 的做法是重写 POI 对 07 版 Excel 的解析逻辑07 版不再把整个解压结果保存在内存里而是采用流式SAX逐行解析显著降低内存峰值。03 版xls依赖 POI 的 SAX 模式但在上层做了模型转换与封装让使用者更加简单方便。这一设计带来的直接收益是数量级的差别一个 3MB 的 Excel 文件用 POI SAX 解析仍需要约 100MB 内存而改用 EasyExcel 可以降到几 MB并且更大的 Excel 文件也不会出现内存溢出。官方文档同时给出了大文件场景下的实测数据README_EN.md 中标注为Using EasyExcel version 3.0.2在 64M 内存的机器上读取一个 75MB、包含 460,000 行 × 25 列的 Excel 文件只需约 20 秒当然也存在速度更快的极速模式但该模式下内存占用会上升到 100M 多一点对应文档中的说明可参考仓库内 README.md 中关于极速模式的提示。说明以上性能数据为 README 原文转述属于官方文档声明具体数值会随硬件环境与版本变化请以实际压测为准。版本支持与升级注意事项支持的 JDK 版本EasyExcel 2 版本支持 Java 7 / Java 6EasyExcel 3 版本支持 Java 8 及以上。跨大版本升级的兼容性提示2 → 3官方文档明确指出不建议跨大版本升级尤其是跨两个大版本从 2 升级到 3 存在一些不兼容点自定义拦截器修改样式使用自定义 interceptor 修改样式可能会出现问题即使编译不报错读取异常包装读取 Excel 时若invoke函数抛出异常3 版本不再额外包装一层ExcelAnalysisException同样编译不报错但行为变化注解默认值变化涉及boolean或某些枚举值的样式等注解增加了默认值编译器会报错按提示修改注解即可。因此官方建议跨大版本升级后务必重新测试相关功能。Maven 依赖引入以 3.0.2 为例README_EN.md 中给出的最新版本依赖坐标如下dependency groupIdcom.alibaba/groupId artifactIdeasyexcel/artifactId version3.0.2/version /dependency在当前仓库中该依赖结构体现为多模块工程easyexcel-core核心读写实现、easyexcel聚合模块对应上述坐标的 artifactId、easyexcel-support与easyexcel-test测试与示例模块各模块的pom.xml可在仓库根目录下找到。快速开始最简单的读三步走思路官方 Quick Start 给出的读 Excel 分三步创建与 Excel 行结构对应的实体对象每个属性对应 Excel 某行中的一个字段参照DemoData由于 EasyExcel 默认一行一行读取 Excel因此需要创建一个逐行回调的监听器参照DemoDataListener调用EasyExcel.read(...)读取即可读取完第一个 sheet 后文件流会自动关闭。代码示例Test public void simpleRead() { String fileName TestFileUtil.getPath() demo File.separator demo.xlsx; // 指定用哪个 class 去读然后读取第一个 sheet文件流会自动关闭 EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet().doRead(); }实体对象与监听器的实现实体对象见仓库 DemoData.java非常简单属性顺序与 Excel 列顺序一致Getter Setter EqualsAndHashCode public class DemoData { private String string; private Date date; private Double doubleData; }监听器见仓库 DemoDataListener.java实现ReadListenerDemoData接口核心是两个回调invoke(DemoData data, AnalysisContext context)每解析到一行数据就会调用在这里做缓存与批量入库doAfterAllAnalysed(AnalysisContext context)所有数据解析完成后调用用于兜底保存最后一批数据。public class DemoDataListener implements ReadListenerDemoData { private static final int BATCH_COUNT 100; private ListDemoData cachedDataList ListUtils.newArrayListWithExpectedSize(BATCH_COUNT); private DemoDAO demoDAO; public DemoDataListener(DemoDAO demoDAO) { this.demoDAO demoDAO; } Override public void invoke(DemoData data, AnalysisContext context) { cachedDataList.add(data); // 达到 BATCH_COUNT 了需要去存储一次数据库防止几万条数据在内存中导致 OOM if (cachedDataList.size() BATCH_COUNT) { saveData(); cachedDataList ListUtils.newArrayListWithExpectedSize(BATCH_COUNT); } } Override public void doAfterAllAnalysed(AnalysisContext context) { saveData(); } private void saveData() { demoDAO.save(cachedDataList); } }这就是 EasyExcel 低内存读取的关键所在数据被逐行解析、批量消费、及时清理而不是全量堆积在内存里。监听器内部通过BATCH_COUNT示例中为 100控制批量入库的频率实际使用时可以调整。读场景的扩展写法仓库中的 ReadTest.java 还给出了多种常用读法可进一步参考JDK8 Lambda 写法since 3.0.0-beta1用PageReadListener配合 lambda 直接消费分页数据默认每批 100 条可在构造参数中调整匿名内部类写法不额外新建 Listener 类一个文件一个 Reader 的写法EasyExcel.read(...).build()得到ExcelReader再用readSheet(0)指定 sheet 读取读取全部或部分 sheetdoReadAll()读取所有 sheet注意doAfterAllAnalysed会在每个 sheet 结束后各调用一次excelReader.read(readSheet1, readSheet2)读取指定 sheet且建议一次性传入多个 sheet避免 03 版 Excel 被重复读取浪费性能按列下标/列名读取配合ExcelProperty注解使用IndexOrNameData日期、数字或自定义格式转换使用DateTimeFormat、NumberFormat注解或自定义转换器默认读转换器由DefaultConverterLoader#loadDefaultReadConverter()加载多行头.headRowNumber(n)指定表头行数不指定时默认按传入 class 的ExcelProperty#value()表头数量推断不传 class 则默认 1 行读取额外信息批注、超链接、合并单元格since 2.2.0-beta1通过.extraRead(CellExtraTypeEnum.COMMENT / HYPERLINK / MERGE)开启读取公式与单元格类型使用CellDataReadDemoDataCellDataDemoHeadDataListener数据转换异常处理参考DemoExceptionListener同步读取不推荐大数据量doReadSync()会把全部数据放到内存中仅适合小文件不创建对象的读不传 class返回ListMapInteger, Stringmap 的 key 为列下标自定义 CSV 分隔符判断CsvReadWorkbookHolder后通过withDelimiter(,)重新设置 CSV 格式。快速开始最简单的写三步走思路写 Excel 更简单只需两步创建实体对象属性对应 Excel 字段参照DemoData调用EasyExcel.write(...)写出默认写到第一个 sheet写完文件流自动关闭。代码示例Test public void simpleWrite() { String fileName TestFileUtil.getPath() write System.currentTimeMillis() .xlsx; // 指定用哪个 class 去写写到第一个 sheet名字为 template文件流会自动关闭 // 如果想使用 03 版xls传入 excelType 参数即可 EasyExcel.write(fileName, DemoData.class).sheet(template).doWrite(data()); }doWrite(data())接收ListDemoData数据列表。仓库中的 WriteTest.java 还给出了多种写场景扩展分页数据写入doWrite(() - data())传Supplier配合数据库分页查询实现大数据量导出ExcelWriter WriteSheet 写法EasyExcel.write(...).build()得到ExcelWriter配合EasyExcel.writerSheet(模板).build()多次写入只导出指定列 / 排除指定列includeColumnFieldNames(Set)/excludeColumnFieldNames(Set)注意使用ExcelProperty时想忽略空列应使用order字段而不是index复杂表头ComplexHeadData配合ExcelProperty多层 value 实现重复多次写入大数据量推荐同一个 sheet 只创建一次WriteSheet循环写入不同 sheet 时每次创建writerSheet(i, 模板 i)且 sheetNo 与 sheetName 必须不同不同对象写不同 sheet 时每次指定.head(DemoData.class)日期/数字/自定义格式转换DateTimeFormat、NumberFormat注解图片导出ImageDemoData支持 byte[]、File、String 路径、InputStream、URL 五种图片来源注意图片会全部放入内存大量图片建议先传 OSS 或用工具压缩超链接、备注、公式、单元格样式WriteCellDataHyperlinkData、CommentData、FormulaData、WriteCellStyle其中富文本与批注等能力需要.inMemory(true)支持模板写入.withTemplate(templateFileName)注意模板文件会全量存内存模板过大可能 OOM不建议用于追加场景列宽行高ColumnWidth、HeadRowHeight、ContentRowHeight注解注解 / 策略 / 拦截器自定义样式注解形式DemoStyleData、策略形式HorizontalCellStyleStrategy头内容分离、AbstractVerticalCellStyleStrategy按列、registerWriteHandler(CellWriteHandler)完全自定义合并单元格注解ContentLoopMerge或LoopMergeStrategy(2, 0)每隔 2 行合并table 写入EasyExcel.writerTable(0).needHead(Boolean.TRUE)同一 sheet 内多表格布局动态表头.head(ListListString)实时生成表头数据也可用ListListString自动列宽不太精确LongestMatchColumnWidthStyleStrategy官方提示数字会导致换行、长度不完全精确可自行参照其实现重写下拉框、超链接等自定义拦截器参考CustomCellWriteHandler、CustomSheetWriteHandler批注写入CommentWriteHandler需.inMemory(Boolean.TRUE)不创建对象的写.head(head()).doWrite(dataList())head 与 data 都用ListList...。从源码看写流程的骨架EasyExcel 的写入口收敛在门面类 EasyExcel.java 中它直接继承EasyExcelFactory源码注释写明 This is actually EasyExcelFactory, and short names look better。在 EasyExcelFactory.java 中可以看到完整的静态工厂方法族写入口write()、write(File)、write(String pathName)、write(OutputStream)以及各自带Class head的重载writerSheet()、writerSheet(Integer sheetNo)、writerSheet(String sheetName)、writerSheet(Integer sheetNo, String sheetName)返回ExcelWriterSheetBuilder读入口read()、read(File)、read(String pathName)、read(InputStream)以及带ReadListener或Class head的重载readSheet()系列返回ExcelReaderSheetBuilder。最终写出由ExcelWriterBuilder→ExcelWriterSheetBuilder构建ExcelWriter核心执行器为 ExcelWriteExecutor 及其实现ExcelWriteAddExecutor样式、合并等能力则通过registerWriteHandler注册的写处理器链CellWriteHandler 系列在写出过程中逐单元格回调生效。Web 场景文件下载与上传在 Spring MVC 场景下EasyExcel 同样提供了开箱即用的支持。完整示例见仓库 WebTest.java。文件下载GetMapping(download) public void download(HttpServletResponse response) throws IOException { // 使用 swagger 可能会出问题请直接用浏览器或 postman 调用 response.setContentType(application/vnd.openxmlformats-officedocument.spreadsheetml.sheet); response.setCharacterEncoding(utf-8); // URLEncoder.encode 可以防止中文文件名乱码与 easyexcel 本身无关 String fileName URLEncoder.encode(test, UTF-8).replaceAll(\\, %20); response.setHeader(Content-disposition, attachment;filename*utf-8 fileName .xlsx); // 直接写注意 finish 的时候会自动关闭 OutputStream外面再关一次流问题不大 EasyExcel.write(response.getOutputStream(), DownloadData.class).sheet(template).doWrite(data()); }要点Content-Type使用 xlsx 的 MIME 类型application/vnd.openxmlformats-officedocument.spreadsheetml.sheet文件名经URLEncoder.encode处理并替换为%20配合Content-disposition的filename*utf-8格式避免中文乱码失败时默认会返回一个含有部分数据的 Excel若希望失败时返回 JSON 错误信息可用autoCloseStream(Boolean.FALSE)阻止自动关闭流捕获异常后response.reset()再输出 JSON参考downloadFailedUsingJson示例since 2.1.1。文件上传PostMapping(upload) ResponseBody public String upload(MultipartFile file) throws IOException { EasyExcel.read(file.getInputStream(), UploadData.class, new UploadDataListener(uploadDAO)).sheet().doRead(); return success; }上传场景把MultipartFile的输入流直接交给EasyExcel.read(...)配合监听器逐行解析入库。注意监听器不要交给 Spring 管理每次读取都要new如果内部需要用到 Spring 的 bean如 DAO通过构造函数传入即可这一点在 DemoDataListener.java 的类注释中也有明确强调。相关文档与更多资源快速开始easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/下的 read、write、web 示例即最佳入门材料更新说明update.md项目简介中文README.md大文件读取专项文档docs/LARGEREAD.mdAPI 参考docs/API.md参与贡献CONTRIBUTING.md测试模块中还覆盖了注解、BOM、单元格数据、字符集、兼容性、转换器、日期格式、加密、异常、排除/包含列、额外信息、填充、处理器、复杂表头、大数据量、多 sheet、无模型、非驼峰、参数、重复读写、排序、样式、模板等专题用例位于 easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core遇到具体问题时可直接搜索对应测试类验证预期行为。小结EasyExcel 通过重写 07 版 Excel 的 POI 解析逻辑把 3MB 文件的解析内存从约 100MB 降到几 MB并从根本上避免大文件内存溢出配合逐行解析 监听器批量消费的读取模型以及EasyExcel.read/write一行式链式 API读写与 Web 上传下载都极其简洁。使用时请务必注意跨大版本升级需重新测试、监听器每次 new、doReadSync()与withTemplate等全量内存操作仅适合小数据量。掌握本文的三种核心用法与仓库中的扩展示例即可在业务中安全地处理大规模 Excel 数据。【免费下载链接】easyexcel快速、简洁、解决大文件内存溢出的java处理Excel工具项目地址: https://gitcode.com/gh_mirrors/ea/easyexcel创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考