为什么又造一个 Excel 库?

发布时间:2026/7/22 9:04:54

为什么又造一个 Excel 库? 生态里 Excel 读写方案已经很成熟了——EPPlus、ClosedXML、MiniExcel、NPOI各有各的适用场景。Magicodes.IE.IO 走的是另一条路零运行时依赖从 ZIP 到 OOXML 全部自研不依赖任何第三方 Excel 库。对于类库作者来说这意味着你的 NuGet 包不会因为引用了 Excel 功能就把一整套依赖链带进下游项目。Magicodes.IE 团队在维护老 Magicodes.IE.Excel基于 EPPlus的过程中吃够了这个苦于是从头实现了一个 零运行时依赖、流式低内存、多 TFM 覆盖 的 Excel I/O 引擎——Magicodes.IE.IO。dotnet add package Magicodes.IE.IO支持 netstandard2.0 / net6.0 / net8.0 / net10.0。net6.0 及以上只依赖 BCL。核心设计哲学零运行时依赖不依赖 EPPlus、不依赖 ClosedXML、不依赖任何第三方 Excel 库。net6.0 目标只引用 BCLnetstandard2.0 只包含少量兼容性 polyfillSystem.Text.Encoding.CodePages 等。这意味着不会引入第三方 Excel 库的版本冲突你的类库可以放心引用 Magicodes.IE.IO不会让下游被迫引入 Excel 依赖NuGet 包体积极小审计面窄2. 流式低内存写入核心写入路径尽量少分配托管内存。我们不构建 DOM 树、不把整个 workbook 加载到内存再序列化——而是边算边写场景 10 万行 4 列 分配量同步 Write(Stream) ~38 ms ~72 KB固定异步 WriteAsync(Stream) ~39 ms ~92 KB便利层 ToBytes() ~39 ms ~8.5 MB物化为 byte[]换句话说同步流式写的 ~72KB 是固定开销内部缓冲、ZIP 头、共享字符串字典与行数无关。1 万行是 68KB10 万行是 72KB——只多了 4.5KB。导 100 万行内存也涨不上去。ToBytes() 因需把整个文件物化成 byte[]分配随数据量线性增长10 万行约 8.5 MB这是设计内权衡。大数据请用 Write(Stream)。完整的异步支持// 写支持 IEnumerable 和 IAsyncEnumerableawait Xlsx.WriteAsync(stream, data); // 已物化集合异步写入await Xlsx.WriteAsync(stream, GetAsyncData()); // IAsyncEnumerable 边查边写await Xlsx.WriteAsync(“/tmp/orders.xlsx”, orders); // 文件路径便利重载// 读支持同步枚举和异步枚举var rows Xlsx.Read(stream).ToList();await foreach (var o in Xlsx.ReadAsync(stream))Console.WriteLine(o.OrderNo);IAsyncEnumerable 路径配合 EF Core 的 AsAsyncEnumerable()可以从数据库流式读取并直接写入 xlsx不需要先把数据全部读到内存。正确性优先每个导出的 xlsx 都经过三层格式校验必需部件齐全[Content_Types].xml、workbook.xml、sheet1.xml、styles.xml、_rels所有 XML/rels 均为 well-formedOpenXML 包关系图完整.rels 引用的每个 target 真实存在同时支持 1900/1904 双日期系统的正确读取自动归一化。五分钟上手零配置导出// 写文件Xlsx.Write(“/tmp/orders.xlsx”, orders);// 写流响应给浏览器Xlsx.Write(Response.Body, orders);// 直接拿到 byte[]var bytes Xlsx.ToBytes(orders);表头 属性名列序 声明序自动识别 string / number / DateTime / bool / enum / struct / record。Fluent Profile 配置var bytes Xlsx.ToBytes(orders, p p.Sheet(“订单表”).Column(x x.OrderNo, c c.WithName(“订单号”).WithWidth(30)).Column(x x.Amount, c c.WithFormat(“0.00”)).Ignore(x x.CreatedAt).WithFreezeHeader(true));多 SheetXlsx.WriteWorkbook(stream,new Sheet(“Orders”, orders),new Sheet(“Items”, items));读取// 同步var rows Xlsx.Read(stream).ToList();// 异步await foreach (var o in Xlsx.ReadAsync(stream))Process(o);属性标注public class Order{[ExporterHeader(Name “订单号”, Width 30)]public string OrderNo { get; set; }[DisplayFormat(DataFormatString 0.00)] public decimal Amount { get; set; } [ExporterHeader(IsIgnore true)] public DateTime CreatedAt { get; set; }}标注优先级fluent .WithName() [ExporterHeader] [Display(Name)] [Description] 属性名。模板导出基于 .xlsx 模板把单元格里的 {{属性名}} 占位符替换为数据值{{#集合}}…{{/集合}} 列表块逐行展开await Xlsx.ExportByTemplateAsync(“template.xlsx”, “output.xlsx”, data);模板原有的样式、合并单元格、图片、公式全部保留。行号、公式引用自动平移。与其他 Excel 库的对比.NET 生态中主流的 Excel 库各有各的侧重点。我们在 BenchmarkDotNet 中跑了 Magicodes.IE.IO、EPPlus、MiniExcel、ClosedXML、OpenXML SDK 的横向对比。参与者简介库 模式 依赖 特点Magicodes.IE.IO 流式 无net6 纯 BCL 自研 ZIP CRC OOXML边算边写MiniExcel 流式 无 轻量性能出色功能克制EPPlus (v5) DOM ImageSharp 等 功能完整商业授权v5ClosedXML DOM 多个 API 优雅功能全面OpenXML SDK DOM 无 微软官方低层 API写入性能对比.NET 10 / Apple M4 / 4 列字符串 × 100k 行Fastest 压缩库 方法 耗时 分配Magicodes.IE.IO Xlsx.ToBytes() 基准 30.3 ms 8.91 MBMagicodes.IE.IO ToBytes() 宽松引用 20.4 ms 7.14 MBMagicodes.IE.IO Source Gen 快路径 21.1 ms 3.60 MBMagicodes.IE.IO Write(Stream) 流式 ~38 ms ~72 KBMiniExcel SaveAs() 149.2 ms 213.69 MBEPPlus ExcelPackage.Save() 455.3 ms 257.31 MBClosedXML XLWorkbook.SaveAs() 678.7 ms 805.99 MB与其他库的倍数对比对比 速度倍数 分配倍数vs MiniExcel 4.9x 更快 24.0x 更小vs EPPlus 15.0x 更快 28.9x 更小vs ClosedXML 22.4x 更快 90.5x 更小上面是 ToBytes() 便利层的对比。若用流式 Write(Stream)Mio 分配仅 ~72KB固定与其他库的差距拉到 3000x。基准项目完整覆盖了 1k / 10k / 50k / 100k 四种数据量、string / number / datetime / boolean / mixed / styled / SST高重复字符串自动去重七个维度、Fastest / NoCompression 两种压缩档位。完整数字执行dotnet run -c Release -f net10.0 --project src/Magicodes.IE.Benchmarks ––filter “XlsxIO_Benchmarks” --job short --memory快速对比各库优势与适用场景维度 Magicodes.IE.IO MiniExcel EPPlus ClosedXML OpenXML SDK运行时依赖 ⭐⭐⭐ 无 ⭐⭐⭐ 无 ⭐ 多个 ⭐ 多个 ⭐⭐ 仅 BCL授权 MIT MIT 商业v5 MIT MIT写入内存 ⭐⭐⭐ 流式 ~72KB ⭐⭐⭐ 流式 ⭐ DOM ⭐ DOM ⭐ DOM写入速度 ⭐⭐⭐ ⭐⭐⭐ ⭐⭐ ⭐⭐ ⭐功能完整度 ⭐⭐ 常用 ⭐⭐ 轻量 ⭐⭐⭐ 全面 ⭐⭐⭐ 全面 ⭐⭐⭐ 底层异步流 ⭐⭐⭐ 原生 ⭐ 有限 ⭐⭐ Task ⭐⭐ Task ⭐⭐ TaskAOT/裁剪 ⭐⭐⭐ SG ⭐⭐ ⭐ ⭐ ⭐⭐API 易用性 ⭐⭐⭐ 单入口 ⭐⭐⭐ 简洁 ⭐⭐ ⭐⭐⭐ 优雅 ⭐ 底层选型建议类库/NuGet 包作者选 Magicodes.IE.IO——零依赖不会给下游带来任何负担轻量导出 / 已有 MiniExcel两者流式性能接近Mio 在异步流和 AOT 上更强复杂格式 / 图表 / 打印 / 高级样式选 EPPlus 或 ClosedXML——功能最全需要底层控制选 OpenXML SDKWeb 应用批量导出 异步流Magicodes.IE.IO 的 IAsyncEnumerable 原生支持是它独有的优势高级功能速览以下功能默认通过 XlsxWriter 底层 API 调用常用场景建议先看 Xlsx.Write() 够不够。数据验证下拉列表using var writer new XlsxWriter(stream, “订单表”);writer.AddDataValidation(new DataValidation(“C2:C1000”,DataValidationType.List, ““已下单,已发货,已完成””));公式Xlsx.ToBytes(items, p p.Column(x x.Qty, c c.WithName(“数量”)).Column(x x.Price, c c.WithName(“单价”)).Column(x x.Total, c c.WithName(“合计”).WithFormula(A{row}B{row})));{row} 自动替换为当前行号1-based展开为 A2B2、A3*B3…共享字符串表SST对大量重复字符串的大文件自动启用 SST 去重Xlsx.ToBytes(orders, p p.WithAutoSst(true));预扫前 64 行字符串去重比例低于 70% 时自动切到 SST减少文件体积。自动筛选、合并单元格、超链接、行过滤var bytes Xlsx.ToBytes(data, p p.Where(x x.Amount 0) // 只导出符合条件的行.WithAutoFilter(“A1:E1”) // 自动筛选.MergeCells(“A1:B1”) // 合并单元格.AddHyperlink(“A1”, “https://…”)); // 超链接表格、打印设置、保护、批注、条件格式、大纲见 tests/Magicodes.IE.IO.Tests/ 目录下的完整示例。覆盖了日常常见的 Excel 操作需求。压缩档位// 默认 FastestXlsx.ToBytes(data);// 纯 CPU 优先时关闭压缩Xlsx.ToBytes(data, options: new XlsxWriteOptions{Compression CompressionLevel.NoCompression});// 文件需要走网络传输、体积优先Xlsx.ToBytes(data, options: new XlsxWriteOptions{Compression CompressionLevel.Optimal});与老 Magicodes.IE.Excel 的区别维度 老 Magicodes.IE.Excel 新 Magicodes.IE.IO第三方依赖 EPPlus 无net6 纯 BCL写入模型 DOM 全量构建 流式边写边序列化读取 EPPlus 封装 自研流式 Reader多 Sheet 复杂 WriteWorkbook(…) 一行异步流 不支持 IAsyncEnumerable 原生支持AOT / 裁剪 不友好 Source Generator 生成无需反射API 入口 多接口多抽象 Xlsx.Write() / Xlsx.Read() 统一静态入口包体积 大 极小net6 零额外依赖

相关新闻