XmlDocument.Save(Stream) 不重置流位置且无视 StreamWriter,需手动 Seek(0) 或改用 XmlWriter;无原生异步支持,须用 SaveAsync(writer);保存到文件前须确保目录存在、权限合法、路径正确。

XmlDocument.Save(Stream) 会清空流的 Position
调用 XmlDocument.Save(Stream) 前,流必须处于可写位置(CanWrite == true),且通常要求 Position == 0 —— 但更关键的是:它**不会自动重置流位置**,也不会帮你跳过已有内容。如果你往一个已写入数据的 MemoryStream 里反复调用 Save(),新 XML 会从当前位置开始覆盖,旧尾部残留、长度错乱是常态。
常见错误现象:XmlException 提示“根元素缺失”,或解析出乱码/截断 XML;实际是流里混了前一次写入的尾巴,或开头被覆盖成二进制垃圾。
- 务必在
Save()前调用stream.Seek(0, SeekOrigin.Begin)清空并重置位置(如果打算复用流) - 如果流是只写的(如
NetworkStream),不能Seek,那就别复用——每次新建流或用XmlWriter精确控制 -
Save(Stream)默认用 UTF-8 编码,不写 BOM;若下游系统强制要求带 BOM,得换XmlWriter+UTF8Encoding(true)
StreamWriter 包裹 MemoryStream 时编码容易出错
有人习惯先用 StreamWriter 包一层 MemoryStream 再传给 XmlDocument.Save(),结果生成的 XML 读出来全是问号或乱码。这是因为 XmlDocument.Save(Stream) **不关心你包没包 StreamWriter**,它直接按字节写入,而 StreamWriter 的缓冲和编码设置会被绕过。
使用场景:你想控制编码(比如必须 UTF-16)、或想提前写注释/声明外的内容。
- 直接传
MemoryStream给Save(),最安全;编码由XmlDeclaration中的encoding属性决定(默认 "utf-8") - 真要定制编码或加 BOM?用
XmlWriter.Create(stream, new XmlWriterSettings { Encoding = new UTF8Encoding(true) }),再调doc.Save(writer) - 别把
StreamWriter当“编码转换器”塞进去——XmlDocument.Save(TextWriter)才认它,Save(Stream)完全无视
异步保存要用 XmlWriter + Stream,XmlDocument 没原生 async Save
XmlDocument 的 Save() 方法全是同步阻塞的,没有 SaveAsync()。想在 ASP.NET Core 或高并发服务里不卡线程?不能硬等,得自己搭异步管道。
性能影响:大 XML(>1MB)在同步 Save() 时可能明显拖慢响应;尤其流是网络或磁盘 IO 时,线程白白挂起。
- 用
XmlWriter.Create(stream, new XmlWriterSettings { Async = true })创建支持异步的 writer - 然后调
await doc.SaveAsync(writer)(注意:.NET Core 3.0+ / .NET 5+ 才有这个扩展方法) - 别忘了
await writer.FlushAsync(),否则最后一点缓冲可能没写出 - 如果目标流本身不支持异步(比如某些自定义
Stream子类),Async = true会静默退化为同步,得实测验证
保存到 FileStream 时权限和路径问题最常被忽略
本地文件保存失败,十次有八次不是 XML 格式问题,而是 FileStream 构造阶段就崩了:目录不存在、无写入权限、文件被占用、路径含非法字符。
错误信息典型长这样:DirectoryNotFoundException、UnauthorizedAccessException、IOException: The process cannot access the file。
- 先确保父目录存在:
Directory.CreateDirectory(Path.GetDirectoryName(filePath)) - 用
FileMode.Create而非FileMode.OpenOrCreate—— 后者在文件只读时会直接抛异常,前者能覆盖 - 避免用
FileShare.None(默认值),多进程/多线程写同一文件时,改成FileShare.Read - 路径别拼接字符串,用
Path.Combine();敏感路径(如%APPDATA%)记得Environment.ExpandEnvironmentVariables()
XML 结构再正确,流打不开就什么都白搭。检查流本身,永远比检查 XmlDocument 内容优先级更高。









