我尝试将非常大的文件(> 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);
}
答案 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}}汇编,并使用您的应用部署该修改版本。