Webapi Streamcontent VS Pushstreamcontent

WebAPI StreamContent vs PushStreamContent

Regarding the memory usage of these both approaches, for StreamContent and PushStreamContent, Web API doesn't buffer the responses. Following snapshot of code is from WebHostBufferPolicySelector. Source code here.

    /// <summary>
/// Determines whether the host should buffer the <see cref="HttpResponseMessage"/> entity body.
/// </summary>
/// <param name="response">The <see cref="HttpResponseMessage"/>response for which to determine
/// whether host output buffering should be used for the response entity body.</param>
/// <returns><c>true</c> if buffering should be used; otherwise a streamed response should be used.</returns>
public virtual bool UseBufferedOutputStream(HttpResponseMessage response)
{
if (response == null)
{
throw Error.ArgumentNull("response");
}

// Any HttpContent that knows its length is presumably already buffered internally.
HttpContent content = response.Content;
if (content != null)
{
long? contentLength = content.Headers.ContentLength;
if (contentLength.HasValue && contentLength.Value >= 0)
{
return false;
}

// Content length is null or -1 (meaning not known).
// Buffer any HttpContent except StreamContent and PushStreamContent
return !(content is StreamContent || content is PushStreamContent);
}

return false;
}

Also PushStreamContent is for scenarios where you need to 'push' data to the stream, where as StreamContent 'pulls' data from the stream. So, for your current scenario of downloading files, using StreamContent should be fine.

Examples below:

// Here when the response is being written out the data is pulled from the file to the destination(network) stream
response.Content = new StreamContent(File.OpenRead(filePath));

// Here we create a push stream content so that we can use XDocument.Save to push data to the destination(network) stream
XDocument xDoc = XDocument.Load("Sample.xml", LoadOptions.None);
PushStreamContent xDocContent = new PushStreamContent(
(stream, content, context) =>
{
// After save we close the stream to signal that we are done writing.
xDoc.Save(stream);
stream.Close();
},
"application/xml");

WebAPI PushStreamContent client read video stream

Lib (available via nuget) Emgu.CV is an C# wrapper on OpenCV and can receive the stream.

using Emgu.CV;

...

private void StartWatching()
{
Task.Run(() =>
{
try
{
capture = new VideoCapture(headConfiguration.CameraStreamingUri.ToString());
capture.ImageGrabbed += Capture_ImageGrabbed;
capture.Start(ExceptionHandler.AlwaysHandle);
}
catch (Exception ex)
{
capture = null;
Logger.Error(ex);
throw ex;
}
});
}

private void Capture_ImageGrabbed(object sender, EventArgs e)
{
if (capture == null || OnNewCameraFrame == null)
{
return;
}

try
{
var frame = new Mat();
if (capture.Retrieve(frame))
{
var output = new ByteImage(frame.Width, frame.Height, frame.Step);

Marshal.Copy(frame.DataPointer, output.Data, 0, output.Height * output.Stride);

OnNewCameraFrame?.Invoke(this, new ValueChangeArgs<ByteImage>(output));
}
}
catch (Exception ex)
{
Logger.Error(ex);
}
}

HttpClient and PushStreamContent

I found the solution to my problem:

I want to set: httpWebRequest.AllowReadStreamBuffering = false;

HttpClient 4.0 does buffering by default and you cannot acces the property AllowReadStreamBuffering, so you have to use HttpWebRequest directly. (Or you can use HttpClinet 4.5, there is the default behaviour 'streaming') see: http://www.strathweb.com/2012/09/dealing-with-large-files-in-asp-net-web-api/ 6. Using HttpClient)

The second problem was fiddler: Fiddler currently only supports streaming of responses and not requests (Fiddler makes HttpWebRequest/HttpClient behaviour unexpected)

The solution that worked for me:

HttpWebRequest httpWebRequest = HttpWebRequest.Create(...)
httpWebRequest.Method = "POST";
httpWebRequest.Headers["Authorization"] = "Basic " + ...;
httpWebRequest.PreAuthenticate = true;
httpWebRequest.AllowWriteStreamBuffering = false;
httpWebRequest.AllowReadStreamBuffering = false;
httpWebRequest.ContentType = "application/octet-stream";
Stream st = httpWebRequest.GetRequestStream();
st.Write(b, 0, b.Length);
st.Write(b, 0, b.Length);
//...
Task<WebResponse> response = httpWebRequest.GetResponseAsync();

var x = response.Result;
Stream resultStream = x.GetResponseStream();
//... read result-stream ...

How to satisfy multiple byte ranges when using PushStreamContent in WebAPI?

You can do it using MultipartContent like this:

public class MyRangeController : ApiController
{
[HttpGet]
public HttpResponseMessage Get()
{
// Create a multi-part content object for the response; note that per RFC spec, subtype must be "byteranges"
// Note that the content type of the over-all response will be "multipart/byteranges"
// We choose to use a GUID string for the separator; it could be anything suitable.
var multipartContent = new MultipartContent("byteranges", Guid.NewGuid().ToString("D"));

// Create the response object and set its content
var response = new HttpResponseMessage(HttpStatusCode.PartialContent) { Content = multipartContent };

foreach (var rangeItemHeaderValue in Request.Headers.Range.Ranges)
{
// Create PushStreamContent object for our current byte range...
var pushStreamContent = new PushStreamContent((stream1, content, arg3) =>
{
// Write to stream1

stream1.Close();
});

// We need to add certain headers to each part of the response
pushStreamContent.Headers.ContentRange = new ContentRangeHeaderValue(rangeItemHeaderValue.From.Value, rangeItemHeaderValue.To.Value, /* total size of the resource */);
pushStreamContent.Headers.ContentType = new MediaTypeHeaderValue(/* Set a content type for each part of the response */);

// Add the part to the multi-part content response
multipartContent.Add(pushStreamContent);
}

return response;
}
}

JSON object creation PushStreamContent


With JSON.NET and it's JsonTextWriter, you can wrap all the items in a JSON object with an array and still stream the result without building everything in memory first.

response.Content =
new PushStreamContent((stream, content, context) =>
{
using (var sw = new StreamWriter(stream))
using (var jsonWriter = new JsonTextWriter(sw))
{
jsonWriter.WriteStartObject();
{
jsonWriter.WritePropertyName("response");
jsonWriter.WriteStartArray();
{
foreach (var item in ps)
{
var jObject = JObject.FromObject(item);
jObject.WriteTo(jsonWriter);
}
}
jsonWriter.WriteEndArray();
}
jsonWriter.WriteEndObject();
}
});


Related Topics



Leave a reply



Submit