使用WebAPI流式传输大型文件(超过IIS 2GB)

我正在尝试将非常大的文件(> 2GB)上传到我的WebAPI应用程序(在.NET 4.5.2上运行,Windows 2012R2)。

设置httpRuntime maxRequestLength属性是没有用的,因为它只能处理小于2GB的文件。

我目前正在使用自定义的MultipartFormDataStreamProvider来读取服务器上的整个流,并且我已经使用自定义WebHostBufferPolicySelector关闭了缓冲。

我发现ASP.NET(或WebAPI)使用Hood下的HttpBufferlessInputStream,它有一个名为_disableMaxRequestLength的字段。 如果我将此值设置为true(通过reflection),我可以流式传输任何大小的文件。

然而,摆弄这些内部的这些显然不是一个好方法。

用于请求的HttpRequest类有一个名为GetBufferlessInputStream的方法,该方法具有允许禁用maxRequestLength的重载。

我的问题是:如何让WebAPI使用此重载而不是标准的重载?

有没有办法替换Default HttpRequest或HttpContext类? 或者我真的需要使用reflection来完成整个过程吗?

这是我目前用来禁用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); } 

好的,我找到了一个非常简单的解决方案 来自@JustinR的答案。 当然会工作的。 但我想继续使用MultipartFormDataStreamProvider,因为它处理所有MIME的东西。

解决方案是简单地使用正确的无缓冲区输入流创建一个新的StreamContent实例,并使用原始内容中的标头填充它:

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

根据MSDN ,读取无限流长度的方法是HttpRequest.GetBufferlessInputStream 。 你可以这样做:

 public void ReadStream(HttpContext context, string filePath) { using (var reader = new StreamReader(context.Request.GetBufferlessInputStream(true))) using (var filestream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.Read, 4096, true)) using (var writer = new StreamWriter(filestream)) { var readBuffer = reader.ReadToEnd(); writer.WriteAsync(readBuffer); } } 

恕我直言,没有简单的方法可以做到这一点。

GetBufferlessInputStream的调用深埋在HttpControllerHandler ,这是ASP.NET Web API的最低层(它是一个HTTP处理程序,在其上构建整个Web API堆栈。

你可以在这里看到代码 。

正如您所看到的那样,它充满了静态,具有嵌套逻辑条件,内部和私有的长方法,所以它根本不可定制。
虽然Web API中的整个HttpControllerHandler理论上可以用自定义实现替换(这是在HttpControllerRouteHandler完成的 – 通过覆盖GetHttpHandler方法),但事实上它是不可能的(你可以尝试在你的应用程序中内化这段代码,但你会结束也拖了很多额外的内部类)。

我想到的最好的事情(我不敢这么说)是修改源HttpControllerHandler类以使用GetBufferlessInputStream的重载来禁用请求长度限制并重新编译System.Web.Http.WebHost程序集,并部署它与您的应用程序的修改版本。