如何在ASP.NET响应中传递大文件?

如何在ASP.NET响应中传递大文件?,asp.net,iis,download,Asp.net,Iis,Download,我不寻找任何替代的流式文件内容从 数据库,确实我在寻找问题的根源,这是 运行文件直到IIS6,我们在经典模式下运行我们的应用程序,现在我们 将IIS升级到7,我们正在管道模式下运行应用程序池,并且 这个问题开始了 我有一个处理程序,在那里我必须按照客户的要求交付大文件。我面临以下问题 文件的平均大小为4到100 MB,所以让我们考虑80MB文件下载情况。 缓冲打开,慢速启动 Response.BufferOutput = True; 这导致文件启动非常缓慢,因为用户下载,甚至进度条直到几秒钟(

我不寻找任何替代的流式文件内容从 数据库,确实我在寻找问题的根源,这是 运行文件直到IIS6,我们在经典模式下运行我们的应用程序,现在我们 将IIS升级到7,我们正在管道模式下运行应用程序池,并且 这个问题开始了

我有一个处理程序,在那里我必须按照客户的要求交付大文件。我面临以下问题

文件的平均大小为4到100 MB,所以让我们考虑80MB文件下载情况。 缓冲打开,慢速启动

Response.BufferOutput = True;
这导致文件启动非常缓慢,因为用户下载,甚至进度条直到几秒钟(通常为3到20秒)才会出现,原因是,IIS首先读取整个文件,确定内容长度,然后开始文件传输。文件在视频播放器中播放,运行速度非常慢,但iPad只下载了部分文件,所以运行速度很快

缓冲关闭、无内容长度、快速启动、无进度

Reponse.BufferOutput = False;
这会导致立即启动,但终端客户端(典型的浏览器,如Chrome)不知道内容长度,因为IIS也不知道,所以它不会显示进度,而是显示下载的xKB

缓冲关闭、手动内容长度、快速启动、进度和协议冲突

Response.BufferOutput = False;
Response.AddHeader("Content-Length", file.Length);
这将导致在Chrome等中立即正确下载文件,但在某些情况下,IIS处理程序会导致“远程客户端关闭连接”错误(这是非常常见的),而其他WebClient会导致协议冲突这发生在所有请求的5%到10%,而不是每个请求。

我想现在发生的是,当我们不进行缓冲时,IIS不会发送任何名为100 continue的内容,客户端可能会断开连接,而不希望有任何输出。然而,从源代码读取文件可能需要更长的时间,但在客户端,我增加了超时时间,但似乎IIS超时,无法控制

我是否可以强制响应发送100继续,而不让任何人关闭连接

更新

我在Firefox/Chrome中发现了以下标题,对于违反协议或错误的标题,这里似乎没有什么不寻常的

Access-Control-Allow-Headers:*
Access-Control-Allow-Methods:POST, GET, OPTIONS
Access-Control-Allow-Origin:*
Access-Control-Max-Age:1728000
Cache-Control:private
Content-Disposition:attachment; filename="24.jpg"
Content-Length:22355
Content-Type:image/pjpeg
Date:Wed, 07 Mar 2012 13:40:26 GMT
Server:Microsoft-IIS/7.5
X-AspNet-Version:4.0.30319
X-Powered-By:ASP.NET
更新2

转向回收仍然没有提供太多,但我已经将我的MaxWorkerProcess增加到8,现在我得到的错误数量比以前少

但平均而言,在一秒钟内的200个请求中,有2到10个请求失败,而且几乎每隔一秒钟就会发生这种情况

更新3

“服务器违反协议。Section=ResponseStatusLine”继续导致5%的请求失败,我有另一个程序从使用WebClient的Web服务器下载内容,每秒出现4-5次此错误,平均有5%的请求失败。是否仍有跟踪WebClient故障的方法

问题重新定义

接收到零字节文件

IIS出于某种原因关闭了连接,在客户端的WebConfig中,我接收到文件的0字节,而不是零字节,我们执行SHA1哈希检查,这告诉我们,在IIS web服务器中,没有记录任何错误

这是我的错误,它在我们使用实体框架时得到了解决,它在读取脏行(未提交的行),因为读取不在事务范围内,将它放在事务范围内解决了这个问题

引发协议冲突异常

WebClient抛出WebException,表示“服务器违反了协议。Section=ResponseStatusLine

我知道我可以启用不安全的头解析,但这不是重点,当我的HTTP处理程序正在发送正确的头时,不知道为什么IIS会发送任何额外的内容(在firefox和chrome上检查,没有异常),这种情况只发生2%

更新4

发现sc-win32 64错误,我在某个地方读到MinBytesPerSecond的WebLimits必须从240更改为0,但一切都是一样的。但是我注意到,每当IIS记录64 sc-win32错误时,IIS都会将HTTP状态记录为200,但有一些错误。现在我无法为200启用失败的跟踪日志记录,因为这将导致大量的fil是的

通过增加MinBytesPerSecond和禁用会话,上述两个问题都得到了解决。我添加了详细的答案,总结了每一点


当您将内容长度和bufferOutput设置为false时,失败的可能原因是IIS尝试gzip您发送的文件,通过设置内容长度,IIS无法将其更改回压缩文件,并且错误开始(*)

因此,
将BufferOutput保持为false,然后对您发送的文件禁用来自iis的gzip
——或者对所有文件禁用iis gzip,然后以编程方式处理gzip部分,将您发送的文件排除在gzip之外

出于同样的原因,一些类似的问题:

(*)为什么不再次更改它?因为从您设置标题的那一刻起,您就无法将其取回,除非您已在IIS上启用此选项,并注意标题尚未全部准备好发送到浏览器

跟进 如果不是gzip,我想到的下一件事是文件被发送了,由于某种原因连接被延迟,超时并关闭了。所以你得到了“远程主机关闭了连接”

根据原因可以解决此问题:

  • 客户端确实关闭了连接
  • 如果使用处理程序,则超时来自页面本身(同样,消息可能必须是“页面超时”)
  • 超时来自空闲等待,页面占用的时间超过执行时间,获得超时并关闭连接。在这种情况下,消息可能是页面超时
  • 池在您发送文件时进行回收。禁用所有池回收!
    <httpRuntime executionTimeout="43200"
    
        if (!File.Exists(myCacheFilePath))
        {
            LoadMyCache(...); // saves the file to disk. don't do this if your source is already a physical file (not stored in a db for example).
        }
    
        // we suppose user-agent (browser) cache is enabled
        // check appropriate If-Modified-Since header
        DateTime ifModifiedSince = DateTime.MaxValue;
        string ifm = context.Request.Headers["If-Modified-Since"];
        if (!string.IsNullOrEmpty(ifm))
        {
            try
            {
                ifModifiedSince = DateTime.Parse(ifm, DateTimeFormatInfo.InvariantInfo);
            }
            catch
            {
                // do nothing
            }
    
            // file has not changed, just send this information but truncate milliseconds
            if (ifModifiedSince == TruncateMilliseconds(File.GetLastWriteTime(myCacheFilePath)))
            {
                ResponseWriteNotModified(...); // HTTP 304
                return;
            }
        }
    
        Response.ContentType = contentType; // set your file content type here
        Response.AddHeader("Last-Modified", File.GetLastWriteTimeUtc(myCacheFilePath).ToString("r", DateTimeFormatInfo.InvariantInfo)); // tell the client to cache that file
    
        // this API uses windows lower levels directly and is not memory/cpu intensive on Windows platform to send one file. It also caches files in the kernel.
        Response.TransmitFile(myCacheFilePath)
    
    protected void PrepareResponseStream(string clientFileName, HttpContext context, long sourceStreamLength)
    {
        context.Response.ClearHeaders();
        context.Response.Clear();
    
        context.Response.ContentType = "application/pdf";
        context.Response.AddHeader("Content-Disposition", string.Format("filename=\"{0}\"", clientFileName));
    
        //set cachebility to private to allow IE to download it via HTTPS. Otherwise it might refuse it
        //see reason for HttpCacheability.Private at http://support.microsoft.com/kb/812935
        context.Response.Cache.SetCacheability(HttpCacheability.Private);
        context.Response.Buffer = false;
        context.Response.BufferOutput = false;
        context.Response.AddHeader("Content-Length", sourceStreamLength.ToString    (System.Globalization.CultureInfo.InvariantCulture));
    }
    
    protected void WriteDataToOutputStream(Stream sourceStream, long sourceStreamLength, string clientFileName, HttpContext context)
    {
        PrepareResponseStream(clientFileName, context, sourceStreamLength);
        const int BlockSize = 4 * 1024 * 1024;
        byte[] buffer = new byte[BlockSize];
        int bytesRead;
        Stream outStream = m_Context.Response.OutputStream;
        while ((bytesRead = sourceStream.Read(buffer, 0, BlockSize)) > 0)
        {
            outStream.Write(buffer, 0, bytesRead);
        }
        outStream.Flush();
    }