资讯动态

C#使用Open XML SDK快速生成Word文档教程

发布时间:2026/10/16 23:51:55 来源:尧图企业网站定制
1. 项目概述作为一名长期从事办公自动化开发的工程师我经常需要处理各种文档生成需求。其中Word文档的自动化创建是最常见的场景之一。很多刚接触C#的开发者面对这个需求时往往不知道从何入手。今天我就来分享一个用C#快速生成Word文档的实战方案这个方案不需要复杂的配置10分钟就能上手。在.NET生态中操作Word文档主要有三种主流方式微软官方的Office Interop、开源的Open XML SDK以及第三方库如Aspose.Words。考虑到易用性和授权成本本教程将重点介绍基于Open XML SDK的方案。这个方案完全免费不依赖Office客户端安装适合大多数中小型项目需求。2. 环境准备与基础配置2.1 开发环境要求首先确保你的开发环境满足以下条件Visual Studio 2019或更高版本社区版即可.NET Framework 4.7.2 或 .NET Core 3.1/ .NET 5基础C#语法知识提示虽然本教程使用Open XML SDK但如果你需要处理复杂格式如页眉页脚、目录等建议考虑商业库如Aspose.Words它们提供了更友好的API。2.2 安装必要的NuGet包在Visual Studio中新建一个控制台应用项目然后通过NuGet包管理器安装以下两个核心组件Install-Package DocumentFormat.OpenXml Install-Package ClosedXML这里我特别推荐配合使用ClosedXML因为它对Open XML SDK进行了高层封装让代码更简洁。例如创建一个带表格的文档用原生Open XML需要50行代码而ClosedXML可能只需10行。3. 核心功能实现3.1 创建空白文档让我们从最简单的场景开始 - 创建一个空白Word文档using DocumentFormat.OpenXml; using DocumentFormat.OpenXml.Packaging; using DocumentFormat.OpenXml.Wordprocessing; public void CreateEmptyDocument(string filePath) { using (WordprocessingDocument doc WordprocessingDocument.Create(filePath, WordprocessingDocumentType.Document)) { // 添加主文档部分 MainDocumentPart mainPart doc.AddMainDocumentPart(); mainPart.Document new Document(); mainPart.Document.AppendChild(new Body()); } }这段代码的核心是WordprocessingDocument类它是操作Word文档的入口点。注意using语句确保资源被正确释放这是很多新手容易忽略的点。3.2 添加文本内容接下来我们给文档添加一些文本public void AddTextToDocument(string filePath, string text) { using (WordprocessingDocument doc WordprocessingDocument.Open(filePath, true)) { MainDocumentPart mainPart doc.MainDocumentPart; Body body mainPart.Document.Body; Paragraph para body.AppendChild(new Paragraph()); Run run para.AppendChild(new Run()); run.AppendChild(new Text(text)); // 设置字体样式 RunProperties runProperties new RunProperties(); runProperties.Append(new Bold()); runProperties.Append(new FontSize() { Val 24 }); run.RunProperties runProperties; } }这里有几个关键对象Paragraph代表一个段落Run一段具有相同格式的文本RunProperties控制文本样式加粗、字号等3.3 插入表格表格是文档中的常见元素下面是添加表格的示例public void AddTableToDocument(string filePath, ListListstring data) { using (WordprocessingDocument doc WordprocessingDocument.Open(filePath, true)) { MainDocumentPart mainPart doc.MainDocumentPart; Body body mainPart.Document.Body; Table table new Table(); // 设置表格边框 TableProperties props new TableProperties( new TableBorders( new TopBorder() { Val new EnumValueBorderValues(BorderValues.Single), Size 4 }, new BottomBorder() { Val new EnumValueBorderValues(BorderValues.Single), Size 4 }, new LeftBorder() { Val new EnumValueBorderValues(BorderValues.Single), Size 4 }, new RightBorder() { Val new EnumValueBorderValues(BorderValues.Single), Size 4 } ) ); table.AppendChild(props); foreach (var rowData in data) { TableRow row new TableRow(); foreach (var cellData in rowData) { TableCell cell new TableCell(); cell.Append(new Paragraph(new Run(new Text(cellData)))); // 设置单元格属性 cell.Append(new TableCellProperties(new TableCellWidth() { Type TableWidthUnitValues.Auto })); row.Append(cell); } table.Append(row); } body.Append(table); } }4. 使用ClosedXML简化代码虽然Open XML SDK功能强大但代码量较大。下面展示如何使用ClosedXML简化操作using ClosedXML.Excel; using DocumentFormat.OpenXml.Packaging; public void CreateDocumentWithClosedXML(string filePath) { using (var doc WordprocessingDocument.Create(filePath, WordprocessingDocumentType.Document)) { var mainPart doc.AddMainDocumentPart(); mainPart.Document new DocumentFormat.OpenXml.Wordprocessing.Document( new Body( new Paragraph( new Run( new Text(Hello World!) ) ) ) ); // 使用ClosedXML添加表格 var table new XLTable(mainPart); table.AddRow(Name, Age); table.AddRow(John, 30); table.AddRow(Alice, 25); table.Insert(); } }5. 高级功能实现5.1 添加页眉页脚public void AddHeaderFooter(string filePath) { using (WordprocessingDocument doc WordprocessingDocument.Open(filePath, true)) { // 添加页眉 HeaderPart headerPart doc.MainDocumentPart.AddNewPartHeaderPart(); string headerId doc.MainDocumentPart.GetIdOfPart(headerPart); headerPart.Header new Header( new Paragraph( new Run( new Text(公司机密文档) ) ) ); // 添加页脚 FooterPart footerPart doc.MainDocumentPart.AddNewPartFooterPart(); string footerId doc.MainDocumentPart.GetIdOfPart(footerPart); footerPart.Footer new Footer( new Paragraph( new Run( new Text(页码)), new Run( new SimpleField() { Instruction PAGE }) ) ); // 将页眉页脚关联到文档 doc.MainDocumentPart.Document.Append( new SectionProperties( new HeaderReference() { Id headerId }, new FooterReference() { Id footerId } ) ); } }5.2 插入图片public void AddImage(string filePath, string imagePath) { using (WordprocessingDocument doc WordprocessingDocument.Open(filePath, true)) { MainDocumentPart mainPart doc.MainDocumentPart; ImagePart imagePart mainPart.AddImagePart(ImagePartType.Jpeg); using (FileStream stream new FileStream(imagePath, FileMode.Open)) { imagePart.FeedData(stream); } string imageId mainPart.GetIdOfPart(imagePart); var element new Drawing( new DW.Inline( new DW.Extent() { Cx 990000L, Cy 792000L }, new DW.EffectExtent() { LeftEdge 0L, TopEdge 0L, RightEdge 0L, BottomEdge 0L }, new DW.DocProperties() { Id 1U, Name Picture 1 }, new DW.NonVisualGraphicFrameDrawingProperties( new A.GraphicFrameLocks() { NoChangeAspect true }), new A.Graphic( new A.GraphicData( new PIC.Picture( new PIC.NonVisualPictureProperties( new PIC.NonVisualDrawingProperties() { Id 0U, Name New Bitmap Image.jpg }, new PIC.NonVisualPictureDrawingProperties()), new PIC.BlipFill( new A.Blip() { Embed imageId }, new A.Stretch(new A.FillRectangle())), new PIC.ShapeProperties( new A.Transform2D( new A.Offset() { X 0L, Y 0L }, new A.Extents() { Cx 990000L, Cy 792000L }), new A.PresetGeometry( new A.AdjustValueList()) { Preset A.ShapeTypeValues.Rectangle })) ) { Uri http://schemas.openxmlformats.org/drawingml/2006/picture }) ) { DistanceFromTop 0U, DistanceFromBottom 0U, DistanceFromLeft 0U, DistanceFromRight 0U }); mainPart.Document.Body.AppendChild(new Paragraph(new Run(element))); } }6. 常见问题与解决方案6.1 文档损坏问题使用Open XML SDK时最常见的错误是生成的文档无法打开。这通常是因为没有正确关闭文档忘记使用using或调用Dispose缺少必要的文档结构如缺少Body元素XML格式不正确解决方案始终使用using语句确保资源释放遵循标准的文档结构使用Open XML SDK 2.5 Productivity Tool验证文档6.2 性能优化处理大型文档时可能会遇到性能问题建议使用OpenXmlWriter替代DOM方式处理大量数据批量操作后一次性保存避免频繁的Part添加/删除操作// 高性能写入示例 using (WordprocessingDocument doc WordprocessingDocument.Create(filePath, WordprocessingDocumentType.Document)) { MainDocumentPart mainPart doc.AddMainDocumentPart(); using (OpenXmlWriter writer OpenXmlWriter.Create(mainPart)) { writer.WriteStartElement(new Document()); writer.WriteStartElement(new Body()); for (int i 0; i 10000; i) { writer.WriteStartElement(new Paragraph()); writer.WriteElement(new Run(new Text(行 i))); writer.WriteEndElement(); } writer.WriteEndElement(); // Body writer.WriteEndElement(); // Document } }6.3 格式兼容性问题不同版本的Word对Open XML的支持可能有差异建议在目标Word版本上测试避免使用太新的特性添加兼容性设置// 设置兼容性 mainPart.DocumentSettingsPart doc.MainDocumentPart.AddNewPartDocumentSettingsPart(); mainPart.DocumentSettingsPart.Settings new Settings( new Compatibility( new CompatibilitySetting() { Name CompatibilitySettingValues.CompatibilityMode, Val 15 } ) );7. 完整示例项目下面是一个完整的控制台应用示例演示了创建包含文本、表格和图片的Word文档using DocumentFormat.OpenXml; using DocumentFormat.OpenXml.Packaging; using DocumentFormat.OpenXml.Wordprocessing; using System; using System.Collections.Generic; using System.IO; class Program { static void Main(string[] args) { string filePath SampleDocument.docx; // 创建文档 CreateDocument(filePath); // 添加内容 AddContentToDocument(filePath); Console.WriteLine($文档已生成: {Path.GetFullPath(filePath)}); } static void CreateDocument(string filePath) { using (WordprocessingDocument doc WordprocessingDocument.Create(filePath, WordprocessingDocumentType.Document)) { // 添加主文档部分 MainDocumentPart mainPart doc.AddMainDocumentPart(); mainPart.Document new Document(); mainPart.Document.AppendChild(new Body()); } } static void AddContentToDocument(string filePath) { using (WordprocessingDocument doc WordprocessingDocument.Open(filePath, true)) { MainDocumentPart mainPart doc.MainDocumentPart; Body body mainPart.Document.Body; // 添加标题 Paragraph titlePara body.AppendChild(new Paragraph()); Run titleRun titlePara.AppendChild(new Run()); titleRun.AppendChild(new Text(月度销售报告)); RunProperties titleProps new RunProperties(); titleProps.Append(new Bold()); titleProps.Append(new FontSize() { Val 32 }); titleRun.RunProperties titleProps; titlePara.ParagraphProperties new ParagraphProperties( new Justification() { Val JustificationValues.Center } ); // 添加日期 Paragraph datePara body.AppendChild(new Paragraph()); datePara.AppendChild(new Run(new Text($生成日期: {DateTime.Now:yyyy-MM-dd}))); // 添加空行 body.AppendChild(new Paragraph(new Run(new Text()))); // 添加表格 Table table new Table(); // 表格边框 TableProperties props new TableProperties( new TableBorders( new TopBorder() { Val BorderValues.Single, Size 4 }, new BottomBorder() { Val BorderValues.Single, Size 4 }, new LeftBorder() { Val BorderValues.Single, Size 4 }, new RightBorder() { Val BorderValues.Single, Size 4 } ) ); table.AppendChild(props); // 表头 TableRow headerRow new TableRow(); headerRow.Append( CreateTableCell(产品名称, true), CreateTableCell(销售量, true), CreateTableCell(销售额, true) ); table.Append(headerRow); // 表格数据 var salesData new List(string, int, decimal) { (产品A, 150, 4500m), (产品B, 89, 3560m), (产品C, 210, 7350m) }; foreach (var item in salesData) { table.Append(new TableRow( CreateTableCell(item.Item1), CreateTableCell(item.Item2.ToString()), CreateTableCell(item.Item3.ToString(C)) )); } body.AppendChild(table); // 添加空行 body.AppendChild(new Paragraph(new Run(new Text()))); // 添加总结段落 Paragraph summaryPara body.AppendChild(new Paragraph()); summaryPara.AppendChild(new Run(new Text(本月销售表现良好特别是产品C销量突出。))); } } static TableCell CreateTableCell(string text, bool isHeader false) { TableCell cell new TableCell(); Paragraph para new Paragraph(); Run run new Run(new Text(text)); if (isHeader) { RunProperties props new RunProperties(); props.Append(new Bold()); run.RunProperties props; para.ParagraphProperties new ParagraphProperties( new Justification() { Val JustificationValues.Center } ); } para.Append(run); cell.Append(para); return cell; } }8. 扩展建议与最佳实践在实际项目中我有几个经过验证的建议模板化设计对于固定格式的文档先手动创建一个模板文档然后使用代码替换其中的内容。这比完全用代码构建格式要高效得多。异常处理添加完善的错误处理特别是文件操作部分try { // 文档操作代码 } catch (OpenXmlPackageException ex) { Console.WriteLine($文档处理错误: {ex.Message}); // 清理可能已损坏的文件 if (File.Exists(filePath)) File.Delete(filePath); }日志记录在关键步骤添加日志便于排查问题。单元测试为文档生成逻辑编写单元测试验证输出文档的结构和内容。资源清理除了使用using语句对于临时文件要有清理机制string tempFile Path.GetTempFileName(); try { // 使用临时文件操作 } finally { if (File.Exists(tempFile)) File.Delete(tempFile); }异步处理对于耗时操作考虑使用异步方法public async Task GenerateDocumentAsync(string filePath) { await Task.Run(() { using (var doc WordprocessingDocument.Create(filePath, WordprocessingDocumentType.Document)) { // 文档生成逻辑 } }); }内存管理处理大型文档时监控内存使用必要时分块处理。文档验证生成后使用Open XML SDK工具验证文档有效性。版本控制明确记录支持的Word版本并在文档属性中设置兼容性。代码组织将文档生成逻辑封装到单独的类或服务中遵循单一职责原则。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑