C # эффективное чтение потокового контента с ограничением на количество чтения - PullRequest
0 голосов
/ 12 февраля 2019

У меня есть случай, когда вызов веб-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();

Мне нужно обработать только первые 1 Мб данных из ответа, поэтому, если ответ меньше 1 Мб, я прочитаю все, но если его больше, я жестко остановлюсьмое чтение на 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();
     }
}

Это самый эффективный способ и как я могу ограничить количество прочитанного (остальное).Я пробовал этот код, и он всегда возвращает пустую строку.Существует также:

ReadAsStringAsync()
ReadAsByteArrayAsync()
ReadAsStreamAsync()
LoadIntoBufferAsync(int size)

Является ли какой-либо из этих методов более эффективным?

Заранее благодарим за любые указатели!

1 Ответ

0 голосов
/ 12 февраля 2019

Я подозреваю, что наиболее эффективный (но все же правильный) способ сделать это, вероятно, что-то вроде этого.Это усложняется тем, что у вас есть ограничение на количество байтов , которые читаются, а не на количество символов , и поэтому мы не можем использовать 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);
    }
}

Обратите внимание, что вы, вероятно, захотите использовать HttpCompletionOption.ResponseHeadersRead, иначе HttpClient все равно загрузит все тело.

Если вы счастливы, ограничив число символы , тогда жизнь станет проще:

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);
}
...