.NET 8升级后CustomXml Part无法通过FeedData更新问题排查
.NET 8升级后Word CustomXml Part更新失效问题
将项目从.NET Framework 4.8升级到.NET 8后,原本更新Word文档CustomXml Part的代码报entries cannot be opened multiple times in update mode错误,调整流的读取方式后,新代码调用FeedData却未生效,返回的仍是旧的CustomXml内容。
旧代码(.NET Framework 4.8下可用)
public byte[] RenewCustomXmlPart(byte[] content, string customXml) { using (m_log.CreateScope()) { MemoryStream mem = CreateMemoryStream(content); if (mem == null) { return null; } using (WordprocessingDocument mergedDocument = WordprocessingDocument.Open(mem, true)) { foreach (CustomXmlPart item in mergedDocument.MainDocumentPart.CustomXmlParts) { XDocument doc = XDocument.Load(new XmlTextReader(item.GetStream())); if (doc.Document == null) { throw new ArgumentNullException(nameof(doc.Document)); } if (doc.Document.Root == null) { throw new ArgumentNullException(nameof(doc.Document.Root)); } XNamespace xn = doc.Document.Root.GetDefaultNamespace(); if (xn.NamespaceName == GlobalDefinitions.CustomXmlBausteinNamespace) { byte[] a = Encoding.UTF8.GetBytes(customXml); MemoryStream m = new MemoryStream(a); item.FeedData(m); } } mem.Flush(); return mem.ToArray(); } } }
新代码(调整后失效)
public byte[] RenewCustomXmlPart(byte[] content, string customXml) { using (m_log.CreateScope()) { using MemoryStream mem = CreateMemoryStream(content); if (mem == null) { return null; } using WordprocessingDocument mergedDocument = WordprocessingDocument.Open(mem, true); foreach (CustomXmlPart item in mergedDocument.MainDocumentPart.CustomXmlParts) { using (Stream stream = item.GetStream(FileMode.Open, FileAccess.Read)) { XDocument doc = XDocument.Load(new XmlTextReader(stream)); if (doc.Document == null) { throw new ArgumentNullException(nameof(doc.Document)); } if (doc.Document.Root == null) { throw new ArgumentNullException(nameof(doc.Document.Root)); } XNamespace xn = doc.Document.Root.GetDefaultNamespace(); if (xn.NamespaceName != GlobalDefinitions.CustomXmlBausteinNamespace) { continue; } } byte[] newCustomXmlBytes = Encoding.UTF8.GetBytes(customXml); using MemoryStream m = new(newCustomXmlBytes); item.FeedData(m); } mem.Flush(); return mem.ToArray(); } }
错误原因分析
- 集合枚举与修改冲突:.NET 8版本的OpenXML SDK对集合枚举的校验更严格,直接遍历
mergedDocument.MainDocumentPart.CustomXmlParts并在循环内修改元素,会导致枚举器无法同步修改后的内容,最终更新无法写入文档包。 - 流操作与文档保存逻辑缺失:原代码未显式调用
mergedDocument.Save(),仅依赖mem.Flush()无法确保OpenXML的修改完全写入内存流;同时读取流后未处理流位置,可能导致后续写入操作异常。
修正方案代码
public byte[] RenewCustomXmlPart(byte[] content, string customXml) { using (m_log.CreateScope()) { using MemoryStream mem = CreateMemoryStream(content); if (mem == null) { return null; } using WordprocessingDocument mergedDocument = WordprocessingDocument.Open(mem, true); // 先转换为列表,避免枚举时修改集合的同步问题 var targetParts = mergedDocument.MainDocumentPart.CustomXmlParts.ToList(); foreach (CustomXmlPart item in targetParts) { XDocument doc; using (Stream stream = item.GetStream()) { doc = XDocument.Load(stream); stream.Position = 0; // 重置流位置,避免影响后续操作 } if (doc.Document?.Root == null) { throw new ArgumentNullException(nameof(doc.Document.Root)); } XNamespace xn = doc.Document.Root.GetDefaultNamespace(); if (xn.NamespaceName != GlobalDefinitions.CustomXmlBausteinNamespace) { continue; } byte[] newCustomXmlBytes = Encoding.UTF8.GetBytes(customXml); using MemoryStream m = new(newCustomXmlBytes); m.Position = 0; // 确保写入流从开头开始 item.FeedData(m); } // 显式保存文档修改到内存流 mergedDocument.Save(); mem.Position = 0; // 重置内存流位置,确保读取完整内容 return mem.ToArray(); } }
内容的提问来源于stack exchange,提问作者Jannik
相关产品推荐
相关产品推荐

