将Response.Filter与Response.TransmitFile一起使用

时间:2010-08-10 01:51:13

标签: c# .net asp.net asp.net-mvc

我正在使用Response.Filter,以便根据HTTP请求标头Accept-Encoding实现流压缩

这是重要的事情:

        if (AcceptEncoding.Contains("deflate") || AcceptEncoding == "*")
        {
             HttpApp.Response.Filter = new DeflateStream(PreviousOutputStream, CompressionMode.Compress);
             HttpApp.Response.AppendHeader("Content-Encoding", "deflate");
        }

大体上按预期工作。但是,我正在使用MVC控制器上的ActionResult来向用户代理提供文件:

        Response.Clear();
        Response.Headers["Content-Type"] = contentType;
        Response.Headers["Content-Length"] = contentLength;

        if (Request.QueryString["dl"] == "1")
        {
            Response.Headers["Content-Disposition"] = "attachment; filename=" + fileInfo.Name;
        }

        Response.Flush();
        Response.TransmitFile(fileInfo.FullName);

更确切地说,操作方法在new EmptyResult()调用后返回Response.TransmitFile()。如果没有Response.Filter修改,这完全符合预期。

在这种情况下,响应实体到达用户代理是乱码且无法理解的。 FireFox的海报插件显示空实体或混乱的实体回来。

2 个答案:

答案 0 :(得分:1)

如果你可以提供帮助,一定要寻找替代方案,因为在ASP.NET中手动进行压缩并不好玩。但是,如果你像我一样头脑冷静,我会向你提交以下内容。

首先:不要使用.NET的内置压缩流类。它们是错误的,可以随机截断流末尾的字节。我一直在使用DotNetZip,效果很好:http://dotnetzip.codeplex.com/

现在,还有一些补充说明:

  • Response.TransmitFile()不适用于响应过滤。
  • Response.BinaryWrite()不能与响应过滤一起使用,因此您不能循环遍历文件的内容并以此方式写出来。
  • Response.OutputStream不能与响应过滤一起使用,因此您无法循环遍历文件的内容并以此方式将其写出来。
  • Response.WriteFile()可以使用响应过滤,但它会将整个文件加载到内存中并将其保留在内存中,直到客户端关闭连接,这对大文件不起作用。
  • 为了让事情变得更有趣:如果将Response.BufferOutput设置为false,则响应过滤将停止工作。 (我只花了几个小时搞清楚)

显然,围绕响应过滤和写入输出流存在很多不同的问题。使用Reflector和大量实验,这是迄今为止我发现的最好的(在各种情况下正确工作的“最佳”)解决方案:

  1. 编写一个扩展编码的类,并将其称为BinaryEncoding。实现所有方法,以便正确复制字符和字节,但当然要进行必要的类型转换。

  2. 将Response.ContentEncoding设置为BinaryEncoding的实例(您可以非常成功地使用单例模式)。

  3. 使用FileStream打开文件。

  4. 创建new StreamReader(fileStream, new BinaryEncoding(), false)。 “false”参数非常重要,它会阻止StreamReader吃掉字节顺序标记并覆盖BinaryEncoding。

  5. 分配一个char []的缓冲区(我发现32KB是一个很好的大小)。

  6. 然后,循环:

    int n = StreamReader.Read(buffer, 0, buffer.Length);
    Response.Write(buffer, 0, n);
    Response.Flush();
    

    直到n为0.

    警告:此方法会导致相当高的CPU使用率。在100兆位LAN上,单个客户端以10MB /秒的速度下载时,一个核心上的CPU使用率约为40-50%。我希望我能找到更好的方法......如果我有Reflector Pro,我可能会找到一个。

答案 1 :(得分:0)

如果您使用的是IIS7或IIS7.5,我建议使用HTTP压缩模块,而不是自己动手。它可能有助于解决问题。

http://technet.microsoft.com/en-us/library/cc771003(WS.10).aspx