1

I m trying to use System.Net.Http for POST requests. I m ok with HTTP response body being in memory but need to obtain MemoryStream for it. One way to do that would be to call HttpContent.GetAsByteArrayAsync() and wrap a MemoryStream on top of it, but I think this would require content to be copied into a separate byte array (since it returns Task of byte[]).

If the response body is already in some internal buffer in HttpContent, is it possible to create MemoryStream on top of that buffer, or return MemoryStream from HttpContent somehow and avoid copying to a separate byte array?

There is also HttpContent.GetAsStreamAsync(), but that returns regular Stream, not MemoryStream. Even though it is probably an instance of MemoryStream already, I suppose it is not safe or a good practice to cast the returned stream to MemoryStream? (since this is implementation detail that could change).

Is there any other way of doing this, or do i have no choice but to copy into byte[] first?

Thanks.

Yevgeniy P
  • 1,480
  • 1
  • 15
  • 23

2 Answers2

5

If you call LoadIntoBufferAsync first, ReadAsStreamAsync returns a readonly MemoryStream:

await req.Content.LoadIntoBufferAsync();
var stream = (MemoryStream) await req.Content.ReadAsStreamAsync();
weichch
  • 9,306
  • 1
  • 13
  • 25
  • Thanks, this seems like the right way, however I couldn't find the doc that mentions that ReadAsStreamAsync would return readonly MemoryStream after LoadIntoBufferAsync is called. Is there any doc or examples from Microsoft on this? – Yevgeniy P Mar 19 '20 at 08:26
  • 1
    I don't think there's docs. If you don't trust the implementation, you could use `ReadAsByteArray()` and create a `MemoryStream` using this constructor `public MemoryStream (byte[] buffer, bool writable);`. This is what `HttpContent` does internally. And it won't copy the buffer. – weichch Mar 19 '20 at 08:36
  • There's source code btw https://github.com/microsoft/referencesource/blob/master/System/net/System/Net/Http/HttpContent.cs#L176 – weichch Mar 19 '20 at 08:38
  • Thanks, yeah from the source code it does look like it returns memory stream. Looks like this is the best way in the circumstances, however not sure if this is reliable with future releases. I guess to be sure i could call ReadAsStreamAsync() and check if the instance is MemoryStream, and if not then copy the returned stream into new MemoryStream. I think ReadAsByteArrayAsync would copy the buffer (at least in the source code it calls MemoryStream.ToArray() which does the copy). – Yevgeniy P Mar 19 '20 at 08:50
  • You could have a unit test covering the cast bit. If you upgrade to a new release which this behavior has been changed. Your unit test will fail, and you will know. – weichch Mar 19 '20 at 09:00
1

If you call LoadIntoBufferAsync first, CopyToAsync can be used to populate a readonly MemoryStream:

var stream = new MemoryStream(req.Content.Headers.ContentLength);
await req.Content.LoadIntoBufferAsync((int)req.Content.Headers.ContentLength);
await req.Content.CopyToAsync(stream);

This implementation doesn't depend on side effects and is supported by the docs in all framework versions: https://learn.microsoft.com/en-us/dotnet/api/system.net.http.httpcontent.loadintobufferasync?view=netframework-4.6.2

Note: I tried to edit the above answer, but couldn't do it... So here you are.

whittet
  • 80
  • 5