Read HttpContent stream until a character limit using StreamReader

5.1k views Asked by At

I am trying to convert the following code that reads the complete string response of a HttpContent into a string, to read only a certain maximum number of characters. Existing code:

private static async Task<string> GetContentStringAsync(HttpContent content)
{
    string responseContent = await content.ReadAsStringAsync().ConfigureAwait(false);
    return responseContent;
}

Code that I have now:

private static async Task<string> GetContentStringAsync(HttpContent content, int ResponseContentMaxLength)
{
    string responseContent;
    Stream responseStream = await content.ReadAsStreamAsync().ConfigureAwait(false);
    using (StreamReader streamReader = new StreamReader(responseStream))
    {
        // responseContent = Data from streamReader until ResponseContentMaxLength
    }

    return responseContent;
}

I am new to StreamReader and HttpContent manipulation. Is there a way to do this?

2

There are 2 answers

0
Peter Duniho On BEST ANSWER

There are a variety of ways to do this. However, IMHO one of the simplest is to create a MemoryStream into which you've read the exact number of bytes you want, and then have the StreamReader object read from that stream instead of the original one.

For example:

private static async Task<string> GetContentStringAsync(HttpContent content, int ResponseContentMaxLength)
{
    string responseContent;
    Stream responseStream = await content.ReadAsStreamAsync().ConfigureAwait(false);

    int totalBytesRead = 0;
    byte[] buffer = new byte[ResponseContentMaxLength];

    while (totalBytesRead < buffer.Length)
    {
        int bytesRead = await responseStream
            .ReadAsync(buffer, totalBytesRead, buffer.Length - totalBytesRead);

        if (bytesRead == 0)
        {
            // end-of-stream...can't read any more
            break;
        }

        totalBytesRead += bytesRead;
    }

    MemoryStream tempStream = new MemoryStream(buffer, 0, totalBytesRead);

    using (StreamReader streamReader = new StreamReader(tempStream))
    {
        // responseContent = Data from streamReader until ResponseContentMaxLength
    }

    return responseContent;
}

The above of course assumes that ResponseContentMaxLength has a value small enough that it is reasonable to allocate a byte[] large enough to temporarily store that many bytes. Since the content returned is going to be of comparable scale, this seems like a reasonable assumption.

But if you don't want to maintain that extra buffer, an alternative approach would be to write a Stream class that reads from an underlying stream object only as many bytes as you specify, and then pass an instance of that (initialized with the ResponseContentMaxLength value) to the StreamReader object. That's quite a lot of extra work as compared to the above though. (Though, I suppose since that's such a useful object, there might be a publicly available implementation already…I know I've written something like that at least a couple of times myself, I just don't happen to have the code handy at the moment).

0
Romonov On

@Peter Duniho: Thanks for the response. I ended up using slightly simpler code:

using(StreamReader streamReader = new StreamReader(responseStream))
{
    char[] responseContentChars = new char[ResponseContentMaxLength];
                    streamReader.Read(responseContentChars, 0, ResponseContentMaxLength);
    string responseContentString = new string(responseContentChars);
    responseContent = responseContentString.Replace("\0", string.Empty);
}

But the above code can lead to errors if the stream is being used by someone else after being read here. The answer selected will take care of others reading the original stream since a new stream is being constructed from the old stream with the content of length ResponseContentMaxLength.