使用Web API流式传输大型文件(在IIS上大于2GB)

时间:2014-09-09 20:42:40

标签: c# asp.net asp.net-web-api

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

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

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

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

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

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

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

有没有办法替换Default HttpRequest或HttpContext类?或者我真的需要对整个事物使用反射吗?

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

3 个答案:

答案 0 :(得分:6)

好的,我找到了一个非常简单的解决方案。来自@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);

答案 1 :(得分:2)

According to 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);
    }
}

答案 2 :(得分:1)

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

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

您可以看到the code here

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

我想到的最好的事情(我不敢这么说)是修改源HttpControllerHandler类以使用禁用请求长度限制的GetBufferlessInputStream重载并重新编译{ {1}}汇编,并使用您的应用部署该修改版本。