C#在限制读取量的情况下高效读取流内容

C#在限制读取量的情况下高效读取流内容,c#,stream,memorystream,C#,Stream,Memorystream,我有一个例子,web API调用返回一个非常大的字符串响应。我的呼吁如下: var multipartContent = new MultipartFormDataContent(); multipartContent.Add(new ByteArrayContent(blobStream.CopyToBytes()), "upload", Path.GetFileName(fileName)); var response = await _

我有一个例子,web API调用返回一个非常大的字符串响应。我的呼吁如下:

var multipartContent = new MultipartFormDataContent();
multipartContent.Add(new ByteArrayContent(blobStream.CopyToBytes()), 
                         "upload", Path.GetFileName(fileName));

var response = await _httpClient.PostAsync("api/v1/textResponse", multipartContent);
int responeLength = response.Content.Headers.ContentLength.HasValue ? 
                    (int)response.Content.Headers.ContentLength.Value : -1;

response.EnsureSuccessStatusCode();
我只需要处理响应中的前1Mb数据,因此如果响应小于1Mb,我将读取所有数据,但如果响应大于1Mb,我将在1Mb时硬停止读取

我正在寻找最有效的方式来做这个阅读。我尝试过以下代码:

// section above...

response.EnsureSuccessStatusCode();

string contentText = null;

if (responeLength < maxAllowedLimit) // 1Mb
{
     // less then limit - read all as string.
     contentText = await response.Content.ReadAsStringAsync();
} 
else {
     var contentStream = await response.Content.ReadAsStreamAsync();
     using (var stream = new MemoryStream())
     {
         byte[] buffer = new byte[5120]; // read in chunks of 5KB
         int bytesRead;
         while((bytesRead = contentStream.Read(buffer, 0, buffer.Length)) > 0)
         {
             stream.Write(buffer, 0, bytesRead);
         }
         contentText = stream.ConvertToString();
     }
}
这些方法中有哪一种更有效


提前感谢您的指点

我怀疑最有效(但仍然正确)的方法可能是这样的。由于您对读取的字节数(而不是字符数)有限制,因此我们不能使用
StreamReader
,因此这一点变得更加复杂。请注意,我们必须注意不要在代码点中间停止阅读——在许多情况下,单个字符用多个字节表示,中途停止将是一个错误。

const int bufferSize = 1024;
var bytes = new byte[bufferSize];
var chars = new char[Encoding.UTF8.GetMaxCharCount(bufferSize)];
var decoder = Encoding.UTF8.GetDecoder();
// We don't know how long the result will be in chars, but one byte per char is a
// reasonable first approximation. This will expand as necessary.
var result = new StringBuilder(maxAllowedLimit);
int totalReadBytes = 0;
using (var stream = await response.Content.ReadAsStreamAsync())
{
    while (totalReadBytes <= maxAllowedLimit)
    {
        int readBytes = await stream.ReadAsync(
            bytes,
            0,
            Math.Min(maxAllowedLimit - totalReadBytes, bytes.Length));

        // We reached the end of the stream
        if (readBytes == 0)
            break;

        totalReadBytes += readBytes;

        int readChars = decoder.GetChars(bytes, 0, readBytes, chars, 0);
        result.Append(chars, 0, readChars);
    }
}

我怀疑最有效(但仍然正确)的方法可能是这样的。由于您对读取的字节数(而不是字符数)有限制,因此我们不能使用
StreamReader
,因此这一点变得更加复杂。请注意,我们必须注意不要在代码点中间停止阅读——在许多情况下,单个字符用多个字节表示,中途停止将是一个错误。

const int bufferSize = 1024;
var bytes = new byte[bufferSize];
var chars = new char[Encoding.UTF8.GetMaxCharCount(bufferSize)];
var decoder = Encoding.UTF8.GetDecoder();
// We don't know how long the result will be in chars, but one byte per char is a
// reasonable first approximation. This will expand as necessary.
var result = new StringBuilder(maxAllowedLimit);
int totalReadBytes = 0;
using (var stream = await response.Content.ReadAsStreamAsync())
{
    while (totalReadBytes <= maxAllowedLimit)
    {
        int readBytes = await stream.ReadAsync(
            bytes,
            0,
            Math.Min(maxAllowedLimit - totalReadBytes, bytes.Length));

        // We reached the end of the stream
        if (readBytes == 0)
            break;

        totalReadBytes += readBytes;

        int readChars = decoder.GetChars(bytes, 0, readBytes, chars, 0);
        result.Append(chars, 0, readChars);
    }
}


不确定您的
ConvertToString
方法是如何实现的,但您可能需要先将MemoryStream的
位置设置回0。另外,请查看
Stream.CopyTo
-无需自行实现。请注意,您不能只获取UTF-8字节流,并在特定字节数后硬停止。你可能最终会在一个代码点中间,这样会产生一个无效的字符串。您可以使用StreamReader,也可以调用
Encoding.GetDecoder()
I@canton7-``var contentStream=wait response.Content.ReadAsByteArrayAsync();var text=Encoding.UTF8.GetString(contentStream,0,_settings.TextSizeUpperLimitBytes);`这就是你的意思吗?看来效果不错!这是一种更有效的方式来抓住一部分溪流吗?@RobMcCabe我没有建议以任何方式、形状或形式这样做。我说过你必须使用
StreamReader
,或者
Encoding.GetEncoder()
。第一部作品是关于溪流的。第二种方法适用于字节数组,但您可以逐位为其提供字节。不确定如何实现
ConvertToString
方法,但您可能需要首先将MemoryStream的
位置设置回0,查看
Stream.CopyTo
-无需自行实现。请注意,您不能只获取UTF-8字节的流,并在一定数量的字节后硬停止。你可能最终会在一个代码点中间,这样会产生一个无效的字符串。您可以使用StreamReader,也可以调用
Encoding.GetDecoder()
I@canton7-``var contentStream=wait response.Content.ReadAsByteArrayAsync();var text=Encoding.UTF8.GetString(contentStream,0,_settings.TextSizeUpperLimitBytes);`这就是你的意思吗?看来效果不错!这是一种更有效的方式来抓住一部分溪流吗?@RobMcCabe我没有建议以任何方式、形状或形式这样做。我说过你必须使用
StreamReader
,或者
Encoding.GetEncoder()
。第一部作品是关于溪流的。第二种方法适用于字节数组,但您可以一点一点地为其提供字节。这让我找到了我的解决方案——希望这是有效的:使用(var stream=wait response.Content.ReadAsStreamAsync()){byte[]dataBytes=new byte[responenegth];stream.Read(dataBytes,0,responeLength);text=Encoding.UTF8.GetString(dataBytes,0,dataBytes.Length);}@RobMcCabe正如我多次尝试解释的,如果您有任何非ascii字符,这是不安全的!这是因为任何非ascii字符在utf-8中都表示为多个字节,如果您在字符中间停止提取字节,您将得到一个无效字符串!您也看不到从st读取了多少字节雷姆,如果整个身体还没有被阅读,你就不会填满你的字节数组!我的答案有点复杂,这是有原因的-你不能简单地消除复杂性,然后仍然期望它工作!@RobMcCabe请尝试理解这一点。我没有把我的答案放在一起,这样你就可以忽略我试图解释的一切。请阅读我的答案我已经写了,并且理解了为什么我的答案说要做它所做的事情。好吧,我现在明白你的意思了,canton7-有意义,实际上我的场景可能有其他字符集,比如ascii!这么好的观点!我将使用上面的示例!谢谢这让我找到了我的解决方案-希望这是有效的:使用(var stream=await response.Content.ReadAsStreamAsync()){byte[]dataBytes=new byte[responeLength];stream.Read(dataBytes,0,responeLength);text=Encoding.UTF8.GetString(dataBytes,0,dataBytes.Length);}@RobMcCabe正如我多次尝试解释的,如果您有任何非ascii字符,这是不安全的!这是因为任何非ascii字符在utf-8中都表示为多个字节,如果您在字符中间停止提取字节,您将得到一个无效字符串!您也看不到从st读取了多少字节如果整个身体还没有被读取,你就不会填满你的字节数组!我的答案有点复杂,这是有原因的——你不能简单地消除这种复杂性,然后继续体验
string result;
using (var reader = new StreamReader(await response.Content.ReadAsStreamAsync()))
{
    char[] chars = new char[maxAllowedLimit];
    int read = reader.ReadBlock(chars, 0, chars.Length);
    result = new string(chars, 0, read);
}