Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Streaming large files (>2GB over IIS) using WebAPI

Tags:

I'm trying to upload very large files (>2GB) to my WebAPI application (Running on .NET 4.5.2, Windows 2012R2).

Setting the httpRuntime maxRequestLength property is of no use because it's only working with files smaller than 2GB.

I'm currently using a custom MultipartFormDataStreamProvider to read the entire stream on the Server and I've already turned off buffering using a custom WebHostBufferPolicySelector.

What I discovered is that ASP.NET (or WebAPI for that matter) uses a HttpBufferlessInputStream under the Hood which has a field called _disableMaxRequestLength. If I set this value to to true (via reflection), I can stream files of any size.

However, fiddling around these with these internas is clearly not a good way.

The HttpRequest class used for the request has a method called GetBufferlessInputStream which has an overload that allows to disable the maxRequestLength.

My question is: How can I get the WebAPI to use this overload instead of the Standard one?

Is there any way to replace the Default HttpRequest or HttpContext class? Or do I really Need to use reflection for the whole stuff?

This is the code I'm currently using to disable the maxRequestLength:

    private void DisableRequestLengthOnStream(HttpContent parent)     {         var streamContentProperty = parent.GetType().GetProperty("StreamContent", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);         if (streamContentProperty == null) return;         var streamContent = streamContentProperty.GetValue(parent, null);         if (streamContent == null) return;         var contentProperty = typeof(StreamContent).GetField("content", BindingFlags.Instance | BindingFlags.NonPublic);         if (contentProperty == null) return;         var content = contentProperty.GetValue(streamContent);         if (content == null) return;         var requestLengthField = content.GetType().GetField("_disableMaxRequestLength", BindingFlags.Instance | BindingFlags.NonPublic);         if (requestLengthField == null) return;         requestLengthField.SetValue(content, true);     } 
like image 559
Henning Krause Avatar asked Sep 09 '14 20:09

Henning Krause


1 Answers

Ok, I found a pretty simple solution. The answer from @JustinR. would work, of course. But I wanted to continue to use a MultipartFormDataStreamProvider because that handles all the MIME stuff.

The solution is to simply create a new StreamContent instance with the correct bufferless Input stream and populate it with the headers from the original Content:

var provider = new MultipartFormDataStreamProvider(path); var content = new StreamContent(HttpContext.Current.Request.GetBufferlessInputStream(true)); foreach (var header in Request.Content.Headers) {     content.Headers.TryAddWithoutValidation(header.Key, header.Value); }  await content.ReadAsMultipartAsync(provider); 
like image 64
Henning Krause Avatar answered Nov 03 '22 14:11

Henning Krause