Magicodes.IE.IO:零依赖、流式低分配的 .NET Excel 读写库
2026/7/14 20:51:17 网站建设 项目流程

Magicodes.IE.IO:零依赖、流式低分配的 .NET Excel 读写库

目录

  • 为什么又造一个 Excel 库?
  • 核心设计哲学
  • 五分钟上手
  • 与其他 Excel 库的对比
  • 高级功能速览
  • 与老 Magicodes.IE.Excel 的区别
  • AOT / NativeAOT 支持
  • 迁移指南
  • 总结

为什么又造一个 Excel 库?

.NET 生态里 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.0net6.0及以上只依赖 BCL。


核心设计哲学

1. 零运行时依赖

不依赖 EPPlus、不依赖 ClosedXML、不依赖任何第三方 Excel 库。net6.0+目标只引用 BCLnetstandard2.0只包含少量兼容性 polyfill(System.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 万行是 68KB,10 万行是 72KB——只多了 4.5KB。导 100 万行,内存也涨不上去。

ToBytes()因需把整个文件物化成byte[],分配随数据量线性增长(10 万行约 8.5 MB),这是设计内权衡。大数据请用Write(Stream)

3. 完整的异步支持

// 写:支持 IEnumerable<T> 和 IAsyncEnumerable<T> await Xlsx.WriteAsync(stream, data); // 已物化集合异步写入 await Xlsx.WriteAsync(stream, GetAsyncData()); // IAsyncEnumerable 边查边写 await Xlsx.WriteAsync("/tmp/orders.xlsx", orders); // 文件路径便利重载 // 读:支持同步枚举和异步枚举 var rows = Xlsx.Read<Order>(stream).ToList(); await foreach (var o in Xlsx.ReadAsync<Order>(stream)) Console.WriteLine(o.OrderNo);

IAsyncEnumerable路径配合 EF Core 的AsAsyncEnumerable(),可以从数据库流式读取并直接写入 xlsx,不需要先把数据全部读到内存。

4. 正确性优先

每个导出的 xlsx 都经过三层格式校验:

  • 必需部件齐全([Content_Types].xmlworkbook.xmlsheet1.xmlstyles.xml_rels)
  • 所有 XML/rels 均为 well-formed
  • OpenXML 包关系图完整(.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));

多 Sheet

Xlsx.WriteWorkbook(stream, new Sheet<Order>("Orders", orders), new Sheet<Item>("Items", items));

读取

// 同步 var rows = Xlsx.Read<Order>(stream).ToList(); // 异步 await foreach (var o in Xlsx.ReadAsync<Order>(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+)DOMImageSharp 等功能完整,商业授权(v5+)
ClosedXMLDOM多个API 优雅,功能全面
OpenXML SDKDOM微软官方,低层 API

写入性能对比

.NET 10 / Apple M4 / 4 列字符串 × 100k 行(Fastest 压缩)

方法耗时分配
Magicodes.IE.IOXlsx.ToBytes()基准30.3 ms8.91 MB
Magicodes.IE.IOToBytes()+ 宽松引用20.4 ms7.14 MB
Magicodes.IE.IOSource Gen 快路径21.1 ms3.60 MB
Magicodes.IE.IOWrite(Stream)流式~38 ms~72 KB
MiniExcelSaveAs()149.2 ms213.69 MB
EPPlusExcelPackage.Save()455.3 ms257.31 MB
ClosedXMLXLWorkbook.SaveAs()678.7 ms805.99 MB

与其他库的倍数对比

对比速度倍数分配倍数
vs MiniExcel4.9x 更快24.0x 更小
vs EPPlus15.0x 更快28.9x 更小
vs ClosedXML22.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.IOMiniExcelEPPlusClosedXMLOpenXML SDK
运行时依赖⭐⭐⭐ 无⭐⭐⭐ 无⭐ 多个⭐ 多个⭐⭐ 仅 BCL
授权MITMIT商业(v5+)MITMIT
写入内存⭐⭐⭐ 流式 ~72KB⭐⭐⭐ 流式⭐ DOM⭐ DOM⭐ DOM
写入速度⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
功能完整度⭐⭐ 常用⭐⭐ 轻量⭐⭐⭐ 全面⭐⭐⭐ 全面⭐⭐⭐ 底层
异步流⭐⭐⭐ 原生⭐ 有限⭐⭐ Task⭐⭐ Task⭐⭐ Task
AOT/裁剪⭐⭐⭐ SG⭐⭐⭐⭐
API 易用性⭐⭐⭐ 单入口⭐⭐⭐ 简洁⭐⭐⭐⭐⭐ 优雅⭐ 底层

选型建议

  • 类库/NuGet 包作者:选Magicodes.IE.IO——零依赖,不会给下游带来任何负担
  • 轻量导出 / 已有 MiniExcel:两者流式性能接近,Mio 在异步流和 AOT 上更强
  • 复杂格式 / 图表 / 打印 / 高级样式:选 EPPlus 或 ClosedXML——功能最全
  • 需要底层控制:选 OpenXML SDK
  • Web 应用批量导出 + 异步流Magicodes.IE.IOIAsyncEnumerable原生支持是它独有的优势

高级功能速览

以下功能默认通过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),展开为A2*B2A3*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 操作需求。

压缩档位

// 默认 Fastest Xlsx.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.ExcelMagicodes.IE.IO
第三方依赖EPPlus(net6+ 纯 BCL)
写入模型DOM 全量构建流式边写边序列化
读取EPPlus 封装自研流式 Reader
多 Sheet复杂WriteWorkbook(...)一行
异步流不支持IAsyncEnumerable<T>原生支持
AOT / 裁剪不友好Source Generator 生成,无需反射
API 入口多接口多抽象Xlsx.Write()/Xlsx.Read()统一静态入口
包体积极小(net6+ 零额外依赖)

AOT / NativeAOT 支持

为 DTO 加一个[XlsxExportable]特性,Source Generator 即生成属性 getter、cell writer、列元数据和 cell setter。读写路径完全不碰反射,适用于 NativeAOT 和剪裁场景:

[XlsxExportable] public class Order { public string OrderNo { get; set; } public decimal Amount { get; set; } } // 和普通用法完全一样 var bytes = Xlsx.ToBytes(orders);

普通 .NET 应用无需标注,直接反射即可——这是渐进式的 AOT 支持。


迁移指南

从老Magicodes.IE.Excel迁移只需三步:

  1. services.AddExcelExporter()注册删掉
  2. 把旧的导出/导入入口替换为Xlsx.Write()/Xlsx.ToBytes()/Xlsx.Read()
  3. ExporterHeaderAttribute仍然兼容;ExportDtoAttribute不再需要(直接用ExportProfile<T>的 fluent API)

总结

Magicodes.IE.IO 解决的核心问题是:不引入 Excel 库依赖的前提下,提供高性能、低分配的 xlsx 读写能力。特别适合:

  • 类库 / NuGet 包作者:零依赖,下游不会因此引入任何 Excel 库
  • Web 应用导出场景:流式写入响应流,内存峰值恒定
  • 大数据导出:配合IAsyncEnumerable实现数据库流式查询 → 直接写 xlsx
  • AOT 场景:Source Generator 消除反射,NativeAOT 友好

仓库地址:github.com/dotnetcore/Magicodes.IE

NuGet:dotnet add package Magicodes.IE.IO

如果对你有帮助,欢迎 ⭐ Star。遇到问题或功能建议,直接提 Issue。也可以加入 QQ 群 85318032 交流(添加时注明 Magicodes.IE)。


许可协议:MIT

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询