WCF REST,流式上传文件和httpRuntime maxRequestLength属性

Nik*_*sev 18 c# streaming wcf file-upload httpwebrequest

我已经创建了一个简单的WCF服务来原型文件上传.服务:

[ServiceContract]
public class Service1
{
    [OperationContract]
    [WebInvoke(Method = "POST", UriTemplate = "/Upload")]
    public void Upload(Stream stream)
    {
        using (FileStream targetStream = new FileStream(@"C:\Test\output.txt", FileMode.Create, FileAccess.Write))
        {
            stream.CopyTo(targetStream);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

它使用webHttpBindingtransferMode设置为"流"和maxReceivedMessageSize,maxBufferPoolSizemaxBufferSize全部设置为2GB.httpRuntimemaxRequestLength设置为10MB.

客户端以下列方式发出HTTP请求:

HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(@"http://.../Service1.svc/Upload");

request.Method = "POST";
request.SendChunked = true;
request.AllowWriteStreamBuffering = false;
request.ContentType = MediaTypeNames.Application.Octet;

using (FileStream inputStream = new FileStream(@"C:\input.txt", FileMode.Open, FileAccess.Read))
{
    using (Stream outputStream = request.GetRequestStream())
    {
        inputStream.CopyTo(outputStream);
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,最后,出了什么问题:

当上传文件100MB大时,服务器返回HTTP 400(错误请求).我试图启用WCF跟踪,但它没有显示错误.当我增加httpRuntime.maxRequestLength到1GB,文件上传没有问题.的MSDN说的是maxRequestLength" 指定输入流缓冲阈值极限,以KB ".

这让我相信整个文件(所有100MB)首先存储在"输入流缓冲区"中,然后它才可用于我的Upload服务器上的方法.我实际上可以看到服务器上的文件大小没有逐渐增加(正如我所料),相反,在它创建的那一刻,它已经是100MB大.

问题:如何让这个工作,以便"输​​入流缓冲区"相当小(比如1MB),当它溢出时,我的Upload方法被调用?换句话说,我希望上传真正流式传输,而不必将整个文件缓冲到任何地方.

编辑: 我现在发现httpRuntime包含另一个与此相关的设置 - requestLengthDiskThreshold.似乎当输入缓冲区超出此阈值时,它不再存储在内存中,而是存储在文件系统上.所以至少整个100MB大文件没有保存在内存中(这是我最害怕的),但是,我仍然想知道是否有一些方法可以完全避免这个缓冲区.

cvl*_*lad 9

如果您使用的是.NET 4并在IIS7 +中托管服务,则可能会受到以下博客文章中描述的ASP.NET错误的影响:

http://blogs.microsoft.co.il/blogs/idof/archive/2012/01/17/what-s-new-in-wcf-4-5-improved-streaming-in-iis-hosting.aspx

基本上,对于流式请求,IIS中的ASP.NET处理程序将在将控制权移交给WCF之前缓冲整个请求.而这个处理程序服从了maxRequestLength限制.

据我所知,该bug没有解决方法,您有以下选择:

  • 升级到.NET 4.5
  • 自托管您的服务而不是使用IIS
  • 使用不基于HTTP的绑定,以便不涉及ASP.NET处理程序


Chr*_*ter 7

这可能是流式实现中的错误.我发现了一篇MSDN文章,建议在http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/fb9efac5-8b57-417e-9f71-35d48d421eb4/上完全按照您的描述进行操作.不幸的是,Microsoft员工建议修复程序在实现中发现了一个错误,并没有跟进修复程序的详细信息.

这表示看起来实现已经破解,您可以通过使用内存分析器分析代码并验证整个文件是否存储在内存中来测试.如果整个文件存储在内存中,您将无法解决此问题,除非有人发现您的代码存在配置问题.

也就是说,虽然使用requestLengthDiskThreshold在技​​术上可以工作,但它会大大增加您的写入时间,因为每个文件必须首先作为临时数据写入,从临时数据读取,再次写入最终文件,最后删除临时数据.正如您已经说过的那样,您正在处理非常大的文件,所以我怀疑这样的解决方案是可以接受的.

您最好的选择是使用分块框架并手动重建文件.我在http://aspilham.blogspot.com/2011/03/file-uploading-in-chunks-using.html找到了有关如何编写此类逻辑的说明,但没有时间检查它的准确性.

对不起,我不能告诉你为什么你的代码没有按照文档记录工作,但类似于第二个例子的东西应该可以在不增加内存占用的情况下工作.