Skip to content

C# / .NET Office 库 — 快速上手

OfficeOxide 是 Rust office_oxide 库的 .NET 绑定。它给 .NET 应用提供高速、对内存友好的 DOCX / XLSX / PPTX / DOC / XLS / PPT 解析、转换与编辑能力 — 底层使用 AOT 兼容的 LibraryImport P/Invoke。

安装

dotnet add package OfficeOxide --version 0.1.0

需要 .NET 8 或 .NET 10。NuGet 包在 runtimes/<rid>/native/ 下随附 win-x64linux-x64linux-arm64osx-x64osx-arm64 的预编译原生库。dotnet publish 会自动把对应的库放在二进制旁边。

读取文档

using OfficeOxide;

using var doc = Document.Open("report.docx");
Console.WriteLine(doc.PlainText());

核心 API

Document 是只读句柄;用 using 释放以归还原生内存。

using OfficeOxide;

using var doc = Document.Open("file.xlsx");

Console.WriteLine(doc.Format);        // "xlsx"
Console.WriteLine(doc.PlainText());
Console.WriteLine(doc.ToMarkdown());
Console.WriteLine(doc.ToHtml());
Console.WriteLine(doc.ToIrJson());

doc.SaveAs("file.docx");              // 目标格式从扩展名推断

阻塞 IO 的异步包装:

using var doc = await Document.OpenAsync("huge.pptx", ct);

从字节打开(无需临时文件):

byte[] data = File.ReadAllBytes("report.docx");
using var doc = Document.FromBytes(data, "docx");

format 必须是 "docx""xlsx""pptx""doc""xls""ppt"

一次性调用的静态辅助:

string text = OfficeOxide.ExtractText("file.docx");
string md   = OfficeOxide.ToMarkdown("file.pptx");
string html = OfficeOxide.ToHtml("file.xlsx");

string? fmt = Document.DetectFormat("mystery.bin"); // 不支持时为 null
Console.WriteLine(Document.Version);                // "0.1.0"

编辑

编辑会保留所有未修改的 OPC 部件(图片、图表、关系)。仅支持 DOCX、XLSX、PPTX。

using OfficeOxide;

using var ed = EditableDocument.Open("template.docx");
long n = ed.ReplaceText("{{name}}", "Alice");
Console.WriteLine($"{n} 处替换");
ed.Save("out.docx");

ReplaceText 返回替换次数(XLSX 返回 0 — 改用 SetCell)。

在 DOCX / PPTX 中替换文本

using var ed = EditableDocument.Open("slides.pptx");
ed.ReplaceText("Q3", "Q4");
ed.ReplaceText("2024", "2025");

byte[] bytes = ed.SaveToBytes();
File.WriteAllBytes("slides_q4.pptx", bytes);

设置 XLSX 单元格(四种重载)

using var wb = EditableDocument.Open("budget.xlsx");

wb.SetCell(0u, "A1", "Total");        // 字符串重载
wb.SetCell(0u, "B1", 42.5);           // double 重载
wb.SetCell(0u, "C1", true);           // bool 重载
wb.SetCellEmpty(0u, "D1");            // 清空

wb.Save("budget.xlsx");

sheetIndex 从 0 开始;cellRef 是标准电子表格记号(A1AA12)。

与格式无关的 IR

ToIrJson() 返回符合 Rust DocumentIR 形状的 JSON 字符串:

using System.Text.Json;

using var doc = Document.Open("report.docx");
string json = doc.ToIrJson();

using var ir = JsonDocument.Parse(json);
foreach (var section in ir.RootElement.GetProperty("sections").EnumerateArray())
{
    if (section.TryGetProperty("title", out var t) && t.ValueKind != JsonValueKind.Null)
        Console.WriteLine(t.GetString());
}

字节流管道

using var http = new HttpClient();
byte[] data = await http.GetByteArrayAsync("https://example.com/file.docx");
using var doc = Document.FromBytes(data, "docx");
Console.WriteLine(doc.ToMarkdown());

旧版格式

using var legacy = Document.Open("old.xls");
legacy.SaveAs("modern.xlsx");

AOT / 裁剪

项目设置了 IsAotCompatible=trueIsTrimmable=true。所有 P/Invoke 使用 LibraryImport 源生成器,因此 dotnet publish -c Release -p:PublishAot=true 会产出单一自包含可执行文件。

错误

失败抛出带类型化 Code 属性的 OfficeOxideException:

try
{
    using var doc = Document.Open("missing.docx");
}
catch (OfficeOxideException ex)
{
    Console.WriteLine($"code={ex.Code} op={ex.Operation}");
}

在已 dispose 的句柄上调用方法会抛 ObjectDisposedException

Code 名称 含义
0 Ok 成功
1 InvalidArg null / 空 / 错误的 format 字符串
2 Io 文件系统错误
3 Parse 文档损坏
4 Extraction 解析成功但渲染失败
5 Internal bug — 请提 issue
6 Unsupported 扩展名/特性不支持

故障排查

症状 解决
DllNotFoundException: office_oxide 原生库没有被复制到二进制旁。请运行 dotnet publish 而不是 dotnet build
Windows 上 BadImageFormatException CPU 架构不匹配 — 部署对应的 win-x64win-arm64 构建。
.docOfficeOxideException 代码 Unsupported 确认扩展名是小写,或通过 FromBytes 显式传 format。
裁剪后符号缺失 OfficeOxide 加入 <TrimmerRootAssembly>
macOS “无法验证开发者” 运行 xattr -d com.apple.quarantine /path/to/liboffice_oxide.dylib 或为 bundle 签名。

相关链接