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

时间:2013-01-15 13:42:41

标签: c# wcf file-upload httpwebrequest streaming

我已经为原型文件上传创建了一个简单的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);
        }
    }
}

它使用webHttpBindingtransferMode设置为“流式”,maxReceivedMessageSizemaxBufferPoolSizemaxBufferSize都设置为2GB。 httpRuntime maxRequestLength设置为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);
    }
}

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

当上传文件100MB大时,服务器返回HTTP 400(错误请求)。我试图启用WCF跟踪,但它没有显示错误。当我将httpRuntimemaxRequestLength增加到1GB时,文件会上传而不会出现问题。 maxRequestLength MSDN says指定输入流缓冲阈值的限制,以KB 为单位。

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

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

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

2 个答案:

答案 0 :(得分: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限制。

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

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

答案 1 :(得分:7)

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

那说看起来好像实现被打破了,您可以通过使用内存分析器分析代码并验证整个文件是否存储在内存中来测试。如果整个文件存储在内存中,您将无法解决此问题,除非有人发现您的代码存在配置问题。

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

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

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