news 2026/7/22 9:04:38

为什么又造一个 Excel 库?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
为什么又造一个 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。

核心设计哲学

  1. 零运行时依赖
    不依赖 EPPlus、不依赖 ClosedXML、不依赖任何第三方 Excel 库。net6.0+ 目标只引用 BCL,netstandard2.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)。

  1. 完整的异步支持
    // 写:支持 IEnumerable 和 IAsyncEnumerable
    await 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,不需要先把数据全部读到内存。

  1. 正确性优先
    每个导出的 xlsx 都经过三层格式校验:

必需部件齐全([Content_Types].xml、workbook.xml、sheet1.xml、styles.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(“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 等 功能完整,商业授权(v5+)
ClosedXML DOM 多个 API 优雅,功能全面
OpenXML SDK DOM 无 微软官方,低层 API
写入性能对比
.NET 10 / Apple M4 / 4 列字符串 × 100k 行(Fastest 压缩):

库 方法 耗时 分配
Magicodes.IE.IO Xlsx.ToBytes() 基准 30.3 ms 8.91 MB
Magicodes.IE.IO ToBytes() + 宽松引用 20.4 ms 7.14 MB
Magicodes.IE.IO Source Gen 快路径 21.1 ms 3.60 MB
Magicodes.IE.IO Write(Stream) 流式 ~38 ms ~72 KB
MiniExcel SaveAs() 149.2 ms 213.69 MB
EPPlus ExcelPackage.Save() 455.3 ms 257.31 MB
ClosedXML 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 ⭐⭐ Task
AOT/裁剪 ⭐⭐⭐ SG ⭐⭐ ⭐ ⭐ ⭐⭐
API 易用性 ⭐⭐⭐ 单入口 ⭐⭐⭐ 简洁 ⭐⭐ ⭐⭐⭐ 优雅 ⭐ 底层
选型建议
类库/NuGet 包作者:选 Magicodes.IE.IO——零依赖,不会给下游带来任何负担
轻量导出 / 已有 MiniExcel:两者流式性能接近,Mio 在异步流和 AOT 上更强
复杂格式 / 图表 / 打印 / 高级样式:选 EPPlus 或 ClosedXML——功能最全
需要底层控制:选 OpenXML SDK
Web 应用批量导出 + 异步流: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),展开为 A2
B2、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 操作需求。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/22 9:02:27

2026全球轮胎市场研究报告

轮胎是由橡胶及多种增强材料制成的圆形柔性部件,用于安装在车辆的车轮轮辋上。轮胎是车辆与地面之间唯一的接触点,在行车过程中起着至关重要的作用,直接影响行驶的安全性、舒适性和性能表现。它不仅提供车辆的牵引力和制动力,还能…

作者头像 李华
网站建设 2026/7/22 8:57:15

Claude Code终端AI编程助手极速入门指南

1. Claude Code 十分钟极速入门指南作为一名长期在终端里摸爬滚打的开发者,第一次接触Claude Code时就被它的高效惊艳到了。这个AI编程助手不像其他工具需要复杂的配置,一条命令就能完成从安装到编写第一行代码的全过程。今天我就带大家走一遍这个神奇的…

作者头像 李华
网站建设 2026/7/22 8:57:08

POP3协议介绍(Post Office Protocol version 3 邮局协议第3版,互联网接收下载邮件标准协议之一)服务器一般不保留邮件,与SMTP协议形成鲜明对比(邮件同步)本地邮件

文章目录POP3协议介绍POP3 的主要特点:POP3 与 IMAP 的区别(常见对比):为什么 Mailpit 会提供 POP3 功能?POP3协议介绍 POP3 的全称是 Post Office Protocol version 3(邮局协议第3版)&#xf…

作者头像 李华
网站建设 2026/7/22 8:57:05

EDMA3高级特性:乒乓缓冲与传输链在嵌入式音频处理中的实战应用

1. 项目概述在嵌入式系统开发,尤其是涉及音频、视频流处理或高速数据采集的领域,数据搬运的效率直接决定了整个系统的性能上限。CPU如果深陷于数据搬移的泥潭,就无法专注于核心的信号处理或控制算法。这时,直接内存访问&#xff0…

作者头像 李华
网站建设 2026/7/22 8:56:32

AI如何提升学术论文投稿通过率与效率

1. 项目背景:学术投稿的痛点与AI解决方案 作为一名在学术圈摸爬滚打多年的研究者,我深知论文投稿过程中的种种煎熬。每次收到"很遗憾通知您..."的邮件,都像被泼了一盆冷水。传统投稿流程中,研究者需要反复修改格式、调整…

作者头像 李华