WCF REST服务以Stream为输入参数时首字符丢失的解决方法
问题:WCF REST服务接收Stream时丢失首字符
我开发了一个以Stream作为输入参数的WCF REST服务,契约定义如下:
[OperationContract] [WebInvoke(Method = "POST", ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.WrappedRequest)] void ImportStream(Stream data);
实现代码为:
public void ImportStream(Stream data) { if (data.ReadByte() != -1) { using (var reader = new StreamReader(data)) { string textRead = reader.ReadToEnd(); } } }
我在Postman中传入的输入文本为:
D_CA,P_AMOUNT,D_AN,D_PE,D_RU,D_ORU,D_AC,,D_GO,D_LE,D_NU,D_CO,D_MU,D_PMU,D_DP,P_COMMENT A,100,,2019.12,S000,S000,XA1810,F00,PACK01,,S002,EUR,ORIG07-01,0000.PACKAGE,,,,,,2019.12, A,100,,2019.12,S000,S000,XA1810,F00,PACK01,,S004,EUR,ORIG07-01,0000.PACKAGE,,,,,,2019.12,
但最终读取到的textRead内容丢失了首字符'D',变为"_CA,P_AMOUNT,D_AN,D_PE,D_RU,D_ORU,D_AC,,D_GO,D_LE,D_NU,D_CO,D_MU,D_PMU,D_DP,P_COMMENT.......",请问该如何修复这个问题?
解决方案
这个问题的根源很清楚:你在判断流是否有数据时调用了data.ReadByte(),这个方法会把流的第一个字节(也就是首字符'D')读取出来并消耗掉,流的位置随即移动到第二个字节。之后StreamReader从当前位置开始读取,自然就跳过了第一个字符。
下面提供几种可行的修复方案,按推荐程度排序:
方案一:直接读取流,无需提前判断
其实StreamReader.ReadToEnd()在流为空时只会返回空字符串,完全不需要提前判断流是否有数据。这种写法最简洁,也彻底避免了流位置的问题:
public void ImportStream(Stream data) { using (var reader = new StreamReader(data)) { string textRead = reader.ReadToEnd(); // 仅在文本非空时处理 if (!string.IsNullOrWhiteSpace(textRead)) { // 执行你的业务逻辑 } } }
方案二:用Peek()判断流是否有数据
如果你确实需要提前判断流是否非空,可以用Peek()方法——它只会查看流的下一个字节,不会移动流的位置,这样既完成了判断,又不会消耗首字符:
public void ImportStream(Stream data) { // Peek()返回-1表示流已到末尾,否则说明有数据可读 if (data.Peek() != -1) { using (var reader = new StreamReader(data)) { string textRead = reader.ReadToEnd(); // 处理文本 } } }
方案三:读取首字节后重置流位置(仅适用于支持查找的流)
如果一定要保留ReadByte()的判断逻辑,那在判断后需要把流的位置重置到起始处。不过要注意,这种方式只适用于支持查找操作的流(比如MemoryStream),如果是网络流这类不支持查找的流,访问Position属性会抛出异常:
public void ImportStream(Stream data) { int firstByte = data.ReadByte(); if (firstByte != -1) { // 将流位置重置到开头 data.Position = 0; using (var reader = new StreamReader(data)) { string textRead = reader.ReadToEnd(); // 处理文本 } } }
综合来看,方案一是最优选择,既简洁又没有潜在的兼容性问题。
内容的提问来源于stack exchange,提问作者IngTun2018
相关产品推荐
相关产品推荐

