简介这份资源面向使用 C# 与 .NET 进行 Word 文档自动化处理的开发者重点解决在无 Microsoft Word 环境下创建、修改和操作文档的需求。包内提供 Spire.Doc 的附加使用 Demo并附带 Spire.License.dll 授权文件可激活无限制使用权限避免文档数量或页数受限。压缩包共 19 个文件约 1.89MB以 cs 源码、dll 组件、sln 解决方案、csproj 工程文件及 resx 资源文件为主另含 exe、pdb、settings 等辅助文件结构完整可直接运行调试。Demo 覆盖插入图片、插入表格、复制行生成新文档等典型场景并延伸至文本格式化、样式模板、邮件合并、文档保护与格式转换等进阶用法。目前已有 3564 人学习下载适合希望快速掌握 Spire.Doc 核心 API、提升文档批处理效率的 C# 开发者参考实践。1. 当 Word 文档处理成了项目里的拦路虎这套 C# 方案能省掉不少折腾做过 .NET 项目的人大概都遇到过这种场景后台要批量生成合同、导出报表、把 HTML 转成 Word 存档或者从一堆上传的 docx 里抽取表格数据。第一反应往往是找 Office Interop结果部署到服务器上就翻车——要么机器上没装 Office要么 COM 组件权限报错要么并发一高直接卡死。后来换成 OpenXML SDK功能是稳了但写起来啰嗦调个页眉页脚、插个水印得翻半天文档。这份资源给的是一个基于 C# 和 Spire.Doc 的完整 Demo把文档创建、内容替换、格式转换、表格操作这些高频需求都封装好了拿来就能跑也方便二次开发。它适合正在做 .NET 后台文档处理、又不想被 Interop 和 OpenXML 折磨的开发者。2. 技术选型与项目结构为什么是 Spire.Doc 而不是 Interop2.1 文档处理库的三种路线对比在 .NET 生态里处理 Word 文档主流就三条路。第一条是 Microsoft.Office.Interop.Word优点是功能全几乎能操作 Word 的所有能力缺点是强依赖本机安装 Office服务器部署成本高COM 释放不及时还会残留进程并发场景基本不可用。第二条是 OpenXML SDK纯托管代码不依赖 Office性能也好缺点是 API 偏底层操作一个段落要层层遍历写起来心智负担重遇到复杂格式容易漏细节。第三条就是 Spire.Doc 这类商业库纯 .NET 实现不依赖 OfficeAPI 设计贴近 Word 对象模型Document、Section、Paragraph、Table 这些概念直接对应上手快。这份 Demo 选的是第三条路。它把 Spire.Doc 的常用能力集中演示了一遍同时因为标题里提到“无限制”说明资源里包含了可用的授权处理方式避免了商业库常见的页数限制和水印问题。对于中小型项目来说这种方案在开发效率和部署便利性之间取了一个不错的平衡点。2.2 项目目录与模块划分拿到源码后先看目录结构。一个典型的 Spire.Doc Demo 项目通常长这样SpireDocDemo/ ├── SpireDocDemo.sln ├── SpireDocDemo/ │ ├── Program.cs // 控制台入口演示各功能调用 │ ├── Services/ │ │ ├── DocumentCreateService.cs // 文档创建 │ │ ├── DocumentReplaceService.cs // 文本替换 │ │ ├── DocumentConvertService.cs // 格式转换 │ │ └── DocumentTableService.cs // 表格操作 │ ├── Models/ │ │ └── ContractModel.cs // 业务数据模型 │ ├── Templates/ │ │ └── contract_template.docx │ ├── Output/ │ │ └── (生成的文档输出目录) │ └── SpireDocDemo.csproj这个划分把不同职责拆到了独立 Service 里Program.cs 只负责串联调用。二次开发时你要加新功能照着现有 Service 的模式扩一个类就行不用动入口逻辑。Templates 目录放模板文件Output 放生成结果职责清晰。2.3 依赖引入与授权初始化项目依赖通过 NuGet 引入核心包是 Spire.Doc。在 csproj 里通常能看到ItemGroup PackageReference IncludeSpire.Doc Version10.x.x / /ItemGroup授权初始化一般放在程序启动最前面只执行一次// Program.cs 入口处 using Spire.Doc; // 应用授权避免生成文档带水印或页数限制 // 具体授权字符串由资源包内提供替换此处占位即可 Spire.License.LicenseProvider.SetLicenseKey(your-license-key-here); Console.WriteLine(Spire.Doc 授权初始化完成);这里的关键点是授权必须在任何 Document 对象创建之前设置否则第一个文档可能已经带上了评估水印。常见做法是把授权字符串放到配置文件或环境变量里不要硬编码进源码提交到仓库。如果你在团队里协作建议单独抽一个 LicenseInitializer 静态类在 Main 方法第一行调用。3. 核心功能拆解从创建文档到格式转换的完整链路3.1 创建文档与段落样式设置最基础的功能是凭空生成一个 Word 文档。Demo 里的 DocumentCreateService 演示了标题、正文、列表的写法using Spire.Doc; using Spire.Doc.Documents; public class DocumentCreateService { public void CreateSimpleDocument(string outputPath) { // 新建一个空白文档 Document document new Document(); // 添加一个节Word 文档内容都挂在节下面 Section section document.AddSection(); // 添加标题段落 Paragraph title section.AddParagraph(); title.AppendText(项目验收报告); // 设置标题样式Heading1 是内置样式 title.ApplyStyle(BuiltinStyle.Heading1); // 居中对齐 title.Format.HorizontalAlignment HorizontalAlignment.Center; // 添加正文段落 Paragraph body section.AddParagraph(); body.AppendText(本报告用于记录项目各阶段的验收结果。); body.Format.FirstLineIndent 21f; // 首行缩进约两个字符 body.Format.LineSpacing 18f; // 行距 // 保存为 docx document.SaveToFile(outputPath, FileFormat.Docx); document.Dispose(); // 释放资源 } }逻辑说明Document 是根对象Section 是页面级容器Paragraph 是段落。ApplyStyle 用的是 Word 内置样式这样生成的文档在 Word 里打开时样式面板能正确识别。FirstLineIndent 的单位是磅21 磅大约对应小四号字的两个字符缩进。LineSpacing 同理。最后必须调用 Dispose否则文件句柄可能被占用后续操作会报“文件被另一进程使用”。参数方面SaveToFile 的第二个参数决定输出格式FileFormat.Docx 是默认选择也可以传 FileFormat.Doc 生成老格式或者 FileFormat.PDF 直接转 PDF。3.2 模板替换批量生成合同的关键实际项目里更常见的是“模板 数据”模式。Demo 用书签和文本替换两种方式演示public class DocumentReplaceService { public void ReplaceByBookmark(string templatePath, string outputPath) { Document document new Document(); document.LoadFromFile(templatePath); // 替换书签内容书签在模板里预先插入 if (document.Bookmarks.Contains(PartyA)) { document.Bookmarks[PartyA].Text 某科技有限公司; } if (document.Bookmarks.Contains(Amount)) { document.Bookmarks[Amount].Text 人民币壹拾万元整; } document.SaveToFile(outputPath, FileFormat.Docx); document.Dispose(); } public void ReplaceByText(string templatePath, string outputPath) { Document document new Document(); document.LoadFromFile(templatePath); // 普通文本替换适合占位符场景 document.Replace({{ContractNo}}, HT-2024-001, false, true); document.Replace({{SignDate}}, DateTime.Now.ToString(yyyy年MM月dd日), false, true); document.SaveToFile(outputPath, FileFormat.Docx); document.Dispose(); } }逻辑说明书签替换适合模板里已经定义好书签位置的场景替换后格式保留得最完整。文本替换更灵活但要注意 Replace 方法的参数第一个是旧文本第二个是新文本第三个表示是否区分大小写第四个表示是否全字匹配。这里传 false 和 true意思是忽略大小写且全字匹配避免把“{{ContractNo}}”里的部分字符误替换。血泪经验文本替换时如果占位符跨了多个 RunWord 里同一段文字可能被拆成多个 Run直接 Replace 可能匹配不到。稳妥做法是先用书签或者在模板里把占位符放在同一个 Run 里。如果遇到替换不生效先检查模板里占位符是不是被 Word 自动拆分了。3.3 格式转换Word 转 PDF 与 HTML格式转换是后台服务的刚需。Demo 里演示了 Word 转 PDF、Word 转 HTMLpublic class DocumentConvertService { public void WordToPdf(string wordPath, string pdfPath) { Document document new Document(); document.LoadFromFile(wordPath); // 直接保存为 PDF document.SaveToFile(pdfPath, FileFormat.PDF); document.Dispose(); } public void WordToHtml(string wordPath, string htmlPath) { Document document new Document(); document.LoadFromFile(wordPath); // 保存为 HTML第二个参数指定导出选项 document.SaveToFile(htmlPath, FileFormat.Html); document.Dispose(); } }逻辑说明转 PDF 是最常用的Spire.Doc 内部做了排版引擎转换质量在同类库里算不错的。转 HTML 时要注意图片默认会导出成单独文件放在同目录如果希望图片内嵌成 base64需要额外配置 HtmlExportOptions。常见做法是转 HTML 后把图片目录一起打包或者用 SaveToStream 配合自定义导出选项。参数方面SaveToFile 的 FileFormat 枚举支持 Docx、Doc、PDF、Html、Xml、Txt、Rtf 等。转 PDF 时如果文档里有特殊字体服务器上没装对应字体可能显示异常建议在服务器上装好常用中文字体。3.4 表格操作读写数据与动态行表格是合同和报表里的重头戏。Demo 演示了创建表格、填充数据、读取表格内容public class DocumentTableService { public void CreateTable(string outputPath) { Document document new Document(); Section section document.AddSection(); // 创建 3 行 4 列的表格 Table table section.AddTable(); table.ResetCells(3, 4); // 填充表头 string[] headers { 序号, 名称, 数量, 单价 }; for (int i 0; i headers.Length; i) { table[0, i].AddParagraph().AppendText(headers[i]); } // 填充数据行 table[1, 0].AddParagraph().AppendText(1); table[1, 1].AddParagraph().AppendText(办公桌); table[1, 2].AddParagraph().AppendText(10); table[1, 3].AddParagraph().AppendText(800); document.SaveToFile(outputPath, FileFormat.Docx); document.Dispose(); } public void ReadTable(string filePath) { Document document new Document(); document.LoadFromFile(filePath); // 遍历所有节和表格 foreach (Section section in document.Sections) { foreach (Table table in section.Tables) { for (int r 0; r table.Rows.Count; r) { for (int c 0; c table.Rows[r].Cells.Count; c) { string text table[r, c].Text; Console.WriteLine($行{r}列{c}: {text}); } } } } document.Dispose(); } }逻辑说明ResetCells 一次性设定行列数比逐行添加效率高。table[r, c] 是索引器直接定位单元格。读取时注意 table[r, c].Text 拿到的是单元格内所有段落的纯文本拼接如果单元格里有多个段落可能需要遍历 Paragraphs 逐个取。踩坑提醒动态添加行时不要用 ResetCells 反复重置那样会清空已有数据。正确做法是 table.AddRow() 追加行或者一开始就按最大行数创建再按需隐藏空行。4. 避坑与常见问题排查那些年我们踩过的文档处理坑4.1 授权未生效导致水印和页数限制现象生成的文档第一页顶部有评估水印或者超过一定页数后内容被截断。 原因授权字符串没设置或者设置时机太晚在 Document 创建之后才调用 SetLicenseKey。 解决把授权初始化放到 Main 方法第一行确保在任何 Document 实例化之前执行。如果用的是配置文件检查读取逻辑有没有异常吞掉。另外注意授权字符串不要有多余空格或换行。4.2 文件被占用导致保存失败现象调用 SaveToFile 时抛异常提示“文件正被另一进程使用”。 原因上一次操作的 Document 对象没有 Dispose文件句柄没释放或者输出路径的文件正被 Word 打开。 解决所有 Document 对象用 using 包裹或者手动在 finally 里 Dispose。输出前检查目标文件是否被占用必要时先删再写。服务器上建议输出到临时目录再移动避免直接覆盖正在被读取的文件。4.3 中文字体在服务器上显示为方框现象本地开发机生成的 PDF 正常部署到服务器后中文变成方框或乱码。 原因服务器上没有安装文档里用到的中文字体Spire.Doc 找不到字体就回退到默认字体而默认字体不含中文字形。 解决在服务器上安装常用中文字体如宋体、黑体、微软雅黑或者在代码里显式指定字体。Linux 服务器可以用 fontconfig 安装字体包。另一个办法是嵌入字体但会增加文件体积。4.4 文本替换匹配不到跨 Run 的占位符现象模板里明明有 {{Name}}Replace 调用后没变化。 原因Word 在保存时可能把 {{Name}} 拆成多个 Run比如 “{{”、“Name”、“}}” 分属不同 RunReplace 按 Run 匹配就找不到完整字符串。 解决优先用书签替代文本占位符。如果必须用文本替换可以在模板里把占位符放在连续输入的一段里避免中途改格式。或者在代码里先合并 Run 再替换但这样会丢失局部格式。4.5 并发场景下的线程安全问题现象多线程同时调用文档生成偶尔出现文件损坏或异常。 原因Spire.Doc 的 Document 对象不是线程安全的多个线程共享同一个 Document 实例会出问题。 解决每个线程独立创建 Document 对象不要复用。如果并发量高考虑用对象池或者队列串行化处理。另外授权初始化是全局的只需执行一次多线程重复设置可能引发竞争。5. 二次开发改造把这套 Demo 变成你自己的文档服务5.1 封装成可注入的文档服务Demo 里的 Service 是直接 new 出来的实际项目里更推荐用依赖注入。把各个 Service 抽成接口注册到 DI 容器public interface IDocumentService { byte[] GenerateFromTemplate(string templatePath, Dictionarystring, string data); byte[] ConvertToPdf(byte[] wordBytes); } public class DocumentService : IDocumentService { public byte[] GenerateFromTemplate(string templatePath, Dictionarystring, string data) { using (Document document new Document()) { document.LoadFromFile(templatePath); foreach (var kv in data) { document.Replace(${{{{{kv.Key}}}}}, kv.Value, false, true); } using (MemoryStream ms new MemoryStream()) { document.SaveToStream(ms, FileFormat.Docx); return ms.ToArray(); } } } public byte[] ConvertToPdf(byte[] wordBytes) { using (Document document new Document()) using (MemoryStream input new MemoryStream(wordBytes)) { document.LoadFromStream(input, FileFormat.Docx); using (MemoryStream output new MemoryStream()) { document.SaveToStream(output, FileFormat.PDF); return output.ToArray(); } } } }这样改造的好处是输入输出都用 Stream 或 byte[]不依赖本地文件路径方便在 Web API 里直接返回文件流。DI 注册时用 AddSingleton 还是 AddScoped 取决于并发量Spire.Doc 本身不持有跨请求状态Singleton 也能用但要注意线程安全。5.2 模板管理的工程化思路模板文件不要散落在代码目录里。常见做法是放到数据库或对象存储用版本号管理。生成时根据业务类型和版本号拉取模板替换后输出。这样模板更新不需要重新部署应用。如果模板量不大也可以放文件系统但要用配置指定根目录不要硬编码相对路径。5.3 性能优化与批量处理批量生成几百份合同时逐个创建 Document 对象开销不小。可以复用一个 Document 实例每次 LoadFromFile 重新加载模板替换后 SaveToStream然后 Reset 再处理下一个。但要注意 Reset 是否彻底释放了上一次的内容。更稳妥的做法还是每个文档独立实例配合并行处理。实测在 4 核机器上并行度设为 CPU 核数时吞吐量比较理想。5.4 验证改造是否成功的检查清单改完之后怎么确认没问题我一般会走一遍这几个检查点生成的文档用 Word 打开看样式、页眉页脚、页码是否正常转 PDF 后检查中文字体和分页批量跑 100 份看内存有没有持续增长并发 10 个请求看有没有文件冲突异常。这套流程走下来基本能覆盖大部分翻车场景。从那以后我每次接入新的文档处理库都强制先跑一遍授权、并发、字体这三个检查点确认没问题再往业务里集成。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?