将不可搜索的流转换为byte []会产生ArgumentOutOfRangeException

时间:2014-03-23 11:23:07

标签: c# httpwebresponse

我正在尝试从{em> GET 请求GetResponseStream并将其转换为byte[] Stream 是不可搜索的,因此我无法访问stream.Lengthresponse.ContentLength不可靠。

private byte[] streamToBytes(Stream stream, int bufferSize = 4096)
    {
        byte[] buffer = new byte[bufferSize];
        int read = 0;
        int pos = 0;
        List<byte> bytes = new List<byte>();
        while (true) { // `bufferSize > read` does **not** mean stream end
            while (bufferSize == (read = stream.Read(buffer, pos, bufferSize))) {
                pos += read;
                bytes.AddRange(buffer);
            }
            if (read > 0) {
                byte[] _buffer = new byte[read];
                Array.Copy(buffer, _buffer, read);
                bytes.AddRange(_buffer);
            } else break;
        }
        return bytes.ToArray();
    }

ArgumentOutOfRangeException循环的第二次迭代中,Read会被while抛出。 MSDN says

  

ArgumentOutOfRangeException 偏移计数为负数。

我知道这不可能是因为偏移pos)是0 + read >= 0 count bufferSize)是4096为什么我会向我抛出异常?

我正在努力使streamToBytes尽可能通用,以便我可以在将来的 async 方法中使用它。


如果请求如何帮助,这里是相关位

HttpWebRequest request = (HttpWebRequest)WebRequest.Create((new Uri("http://google.com")).ToString());
request.Method = "GET";
request.KeepAlive = true;
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
Stream stream = response.GetResponseStream();
byte[] responseBytes = streamToBytes(stream);

2 个答案:

答案 0 :(得分:3)

作为一个更简单的选择:

using(var memStream = new MemoryStream())
{
    stream.CopyTo(memStream);
    return memStream.ToArray();
}

您的代码有两个错误:

  1. offset Read是缓冲区的偏移量,而不是流中的位置(正如@Ulugbek已经注意到的那样)。这意味着您不再需要pos变量。
  2. 即使在流程结束之前,您也不能认为bufferSize == read。您需要检查> 0
  3. 所以你的阅读循环变为:

    while ((read = stream.Read(buffer, 0, bufferSize)) > 0)
    {
        bytes.AddRange(buffer.Take(read));
    }
    

    您现在可以删除最后一个块的特殊处理。将您的代码简化为:

    private byte[] streamToBytes(Stream stream, int bufferSize = 4096)
    {
        byte[] buffer = new byte[bufferSize];
        int read = 0;
        List<byte> bytes = new List<byte>();
        while ((read = stream.Read(buffer, 0, bufferSize)) > 0)
        {
            bytes.AddRange(buffer.Take(read));
        }
        return bytes.ToArray();
    }
    

    使用List<byte>代替MemoryStream也不是一个好主意。 AddRange需要单独迭代字节,而不是使用低级复制操作。用内存流替换列表,代码变为:

    private byte[] streamToBytes(Stream stream, int bufferSize = 4096)
    {
        byte[] buffer = new byte[bufferSize];
        int read = 0;
        using(var bytes = new MemoryStream())
        {
            while ((read = stream.Read(buffer, 0, bufferSize)) > 0)
            {
                bytes.Write(buffer, 0, read);
            }
            return bytes.ToArray();
        }
    }
    

    你甚至可以将它分成两部分,一部分复制到内存流中,另一部分处理内存流的创建并将其转换为字节数组:

    private static void CopyStream(Stream source, Stream destination, int bufferSize = 4096)
    {
        byte[] buffer = new byte[bufferSize];
        int read = 0;
        while ((read = source.Read(buffer, 0, bufferSize)) > 0)
        {
            destination.Write(buffer, 0, read);
        }
    }
    
    private static byte[] StreamToBytes(Stream stream, int bufferSize = 4096)
    {
        using(var memStream = new MemoryStream())
        {
            CopyStream(stream, memStream);
            return memStream.ToArray();
        }
    }
    

    这可能与Stream.CopyTo内部的内容非常相似。

答案 1 :(得分:1)

更改

stream.Read(buffer, pos, bufferSize)

stream.Read(buffer, 0, bufferSize)