为什么StreamReader.ReadToEnd起作用,但Stream.Read不起作用?

时间:2019-10-23 13:26:25

标签: c# asp.net-core .net-core

我试图在ASP.NET Core控制器中以byte[]数组的形式获取请求的正文。这是我最初写的:

var declaredLength = (int)request.ContentLength;
byte[] fileBuffer = new byte[declaredLength];

request.Body.Read(fileBuffer, 0, declaredLength);

此代码有效,但仅适用于较小的请求(约20KB)。对于较大的请求,它会填充数组中的前20,000个字节左右,然后数组的其余部分为空。

我在顶部的答案here中使用了一些代码,并且在重写代码后能够成功读取整个请求正文:

var declaredLength = (int)request.ContentLength;
byte[] fileBuffer = new byte[declaredLength];

// need to enable, otherwise Seek() fails
request.EnableRewind();

// using StreamReader apparently resolves the issue
using (var reader = new StreamReader(request.Body, Encoding.UTF8, true, 1024, true))
{
    reader.ReadToEnd();
}

request.Body.Seek(0, SeekOrigin.Begin);
request.Body.Read(fileBuffer, 0, declaredLength);

为什么StreamReader.ReadToEnd()无法成功读取整个请求正文,而Stream.Read()无法读取?两次读取请求流感觉就像是黑客。有没有更好的方法来解决这个问题? (我只需要将流读入字节数组一次)

1 个答案:

答案 0 :(得分:2)

请记住,您正在尝试阅读request.Body,但尚未收到所有请求。

Stream.Read的行为如下:

  1. 如果已到达流的末尾,请返回0
  2. 如果没有尚未读取的可用字节,请阻塞直到至少有1个字节可用
  3. 如果有1个或更多新字节可用,请立即将其返回。不要阻止。

如您所见,如果尚未接收到整个身体,request.Body.Read(...)只会返回已接收到的身体部分。

StreamReader.ReadToEnd()调用Stream.Read in a loop,直到找到流的结尾。

您可能还应该在循环中调用Stream.Read,直到您读完所有字节为止:

byte[] fileBuffer = new byte[declaredLength];
int numBytesRead = 0;
while (numBytesRead < declaredLength)
{
    int readBytes = request.Body.Read(fileBuffer, numBytesRead, declaredLength - numBytesRead);
    if (readBytes == 0)
    {
        // We reached the end of the stream before we were expecting it
        // Might want to throw an exception here?
    }
    numBytesRead += readBytes;
}