流长度不支持异常抛出

36

当我尝试对传入WCF方法的Stream对象执行stream.Length操作时,出现了错误。

Unhandled Exception!
 Error ID: 0
 Error Code: Unknown
 Is Warning: False
 Type: System.NotSupportedException
 Stack:    at System.ServiceModel.Dispatcher.StreamFormatter.MessageBodyStream.get_Length()

如何获取流的长度?有示例吗?


Stream类是抽象的,你使用的是哪个派生的Stream类?如果可能的话,请发布导致问题的代码。 - khai_khai
7个回答

40

Stream.Length 仅适用于支持寻址的 Stream 实现。通常可以通过检查 Stream.CanSeek 是否为 true 来确定。由于流式传输,许多流的本质是无法预先知道长度的。

如果必须知道长度,则可能需要实际缓冲整个流,在先加载到内存中。


5
HttpClient.GetStreamAsync(url) 方法是什么?它会设置 Length 属性,但不支持随机访问。我需要在 try-catch 代码块中测试 Stream.Length 属性的值吗? - Bruno Zell
在HTTP请求/响应流的情况下:不能信任“Content-Length”头(“永远不要相信[客户端|管道的另一端]!”):连接可能会中止,或者服务器可能会向TCP连接推送比客户端预期更多的数据。 - Dai

14

这就是我所做的事情:

// Return the length of a stream that does not have a usable Length property
public static long GetStreamLength(Stream stream)
{
    long originalPosition = 0;
    long totalBytesRead = 0;

    if (stream.CanSeek)
    {
        originalPosition = stream.Position;
        stream.Position = 0;
    }

    try
    {
        byte[] readBuffer = new byte[4096];
        int bytesRead;

        while ((bytesRead = stream.Read(readBuffer, 0, 4096)) > 0)
        {
            totalBytesRead += bytesRead;
        }

    }
    finally
    {
        if (stream.CanSeek)
        {
            stream.Position = originalPosition;
        }
    }

    return totalBytesRead;
}

4
请使用类型long来表示totalBytesRead,以防您的文件大小可能超过2GB(约为int.MaxValue)。 - Franz B.

12
我在使用WCF服务时遇到了相同的问题。我需要获取POST消息的内容,并在我的方法中使用Stream参数来获取消息正文的内容。一旦我获得了流,我想一次性读取其内容,并需要知道我需要多大的字节数组。在分配数组时,我会调用System.IO.Stream.Length并得到OP提到的异常。您需要知道流的长度是因为要读取整个流的内容吗?
实际上,您可以使用System.IO.StreamReader将整个流的内容读入字符串中。如果您仍然需要知道流的大小,则可以获取生成字符串的长度。以下是我解决此问题的代码:
    [OperationContract]
    [WebInvoke(UriTemplate = "authorization")]
    public Stream authorization(Stream body)
    {
        // Obtain the token from the body
        StreamReader bodyReader = new StreamReader(body);
        string bodyString = bodyReader.ReadToEnd();
        int length = bodyString.Length; // (If you still need this.)
       // Do whatever you want to do with the body contents here. 
    }

7

有时候你无法获取流的长度。在网络流的情况下,唯一找出长度的方法是读取数据直到关闭它。

你想做什么?你可以从流中读取数据,将数据复制到MemoryStream中直到全部读取完毕?


4
@Nevin:流还有什么其他情况吗?基本上,如果您不通过从中读取来更改状态,就无法获取此信息。 - Jon Skeet

6

如果流不支持查找,那么获取流的长度并不总是可能的。请参见Stream类上的异常表。

例如,连接到另一个进程(网络流、标准输出等)的流可以产生任意数量的输出,这取决于另一个进程的编写方式,框架无法确定有多少数据。

一般情况下,您只需读取所有数据直到流的末尾,然后计算已读取的数据量。


0
TcpClient.EndRead() 应该返回流中的字节数。
--编辑,当然你需要使用 TCP 流。

0
我将流复制到新的流中,对我来说有效。
Stream stream = new MemoryStream();
await streamthatcausingerror.CopyToAsync(stream); 


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接