WCF流式传输XML文件必须使用唯一Stream参数、TransferMode.Streamed绑定、禁用MTOM,并通过临时文件或Raw模式解析,否则将静默退入缓冲模式导致内存溢出。

WCF 服务接收 XML 文件,本质是接收二进制流(Stream),而非直接反序列化为 XDocument 或 XmlDocument —— 否则就失去大文件处理能力,且极易触发 OutOfMemoryException。流式传输不是“可选优化”,而是处理 >10 MB XML 文件的**唯一可行路径**。
OperationContract 必须严格满足流式契约规则
WCF 的流式传输在编程模型层由契约强制约束,违反任一条件都会静默退回到缓冲模式(即整个 XML 加载进内存),且无警告。
-
Stream参数或返回值必须是**唯一参数/返回值**:不能带其他string fileName、int timeout等辅助参数 - 若需传递元数据(如文件名、编码、校验码),只能通过
MessageHeader或 URL 查询参数(HTTP 场景)传入 - 服务端方法签名示例(合法):
[OperationContract] void UploadXmlFile(Stream xmlStream);
- 错误写法(触发缓冲):
void UploadXmlFile(Stream xmlStream, string filename)—— 编译通过,但运行时失效
Binding 的 TransferMode 必须显式设为 Streamed
仅改契约不够。默认所有绑定(如 BasicHttpBinding)都是 TransferMode.Buffered,此时即使参数是 Stream,WCF 仍会把整个 XML 读进内存再交给你的方法。
- 必须在服务端和客户端**两端同步设置**:
TransferMode.Streamed(双向流)、StreamedRequest(仅上传)、StreamedResponse(仅下载) - HTTP 场景下,
BasicHttpBinding和CustomBinding支持该属性;NetTcpBinding同样支持 - 配置示例(app.config):
-
maxReceivedMessageSize必须同步调大(单位字节),否则超限直接抛QuotaExceededException
XML 文件不能直接用 XmlReader.Read() 读取原始 Stream
WCF 流式传输的 Stream 是经过 SOAP 封装的(尤其启用 MTOM 时),它不是裸 XML 字节流 —— 直接丢给 XmlReader.Create(stream) 会报 Root element is missing 或乱码。
- 正确做法:先用
MessageEncoder解包,或更实际地——**禁用 SOAP 封装,改用 Raw 模式** - 在绑定中启用
WebHttpBinding+[WebInvoke],并设置BodyStyle = WebMessageBodyStyle.Bare - 或使用自定义
MessageEncoder提取原始 XML 字节(复杂,仅必要时) - 最简方案(推荐):服务端接收
Stream后,先保存为临时文件,再用XmlReader.Create(File.OpenRead(tempPath))安全解析
MTOM 编码对 XML 传输的实际影响
MTOM(Message Transmission Optimization Mechanism)专为含二进制内容的 SOAP 消息设计,但它对纯文本 XML 文件**几乎无收益,反而增加解析负担**。
- MTOM 要求操作契约**只能有单个
Stream参数/返回值**(与前述规则重叠),且必须配合text/xml或application/soap+xml以外的message/unknownMIME 类型 - 对 UTF-8 XML,MTOM 附件机制会引入 Base64 编码开销(+33%体积)+ SOAP 包裹头,整体比原始 HTTP POST 更慢
- 除非你的“XML 文件”实际是 ZIP 压缩包或含 Base64 内嵌图片的富文档,否则应禁用 MTOM,用普通
Stream+WebHttpBinding更轻量
真正卡住多数人的不是“怎么写”,而是“为什么没生效”——WCF 流式传输失败时不会报错,只是悄悄吃掉内存。务必用 Process Explorer 观察服务进程私有字节数,上传 100MB 文件时若内存上涨超 150MB,说明流式未生效,立刻回查 TransferMode 和契约参数数量。









