简体   繁体   English

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

[英]Using Response.Filter with Response.TransmitFile

I'm using Response.Filter in order to implement stream compression in accordance with HTTP Request Header Accept-Encoding 我使用Response.Filter以便根据HTTP请求标头Accept-Encoding实施流压缩

Here's the important stuff: 这是重要的东西:

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

By and large this works as intended. 总的来说,这是可以预期的。 However, I'm in a situation where I'm using an ActionResult on an MVC Controller to serve up files to the user agent: 但是,我处于在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);

To be more exact, the action method returns new EmptyResult() after the Response.TransmitFile() call. 更确切地说,该动作方法在Response.TransmitFile()调用之后返回new EmptyResult() This works exactly as intended without the Response.Filter modification. 无需修改Response.Filter即可完全按预期工作。

In this situation, the response entity reaches the user agent garbled and unintelligible. 在这种情况下,响应实体到达用户代理时出现乱码和难以理解。 FireFox's Poster addon shows empty entities or jumbled entities coming back. FireFox的Poster插件显示了空的实体或混乱的实体回来。

If you can help it, definitely look for alternatives, because manually doing compression in ASP.NET is NOT fun. 如果可以帮助,一定要寻找替代方法,因为在ASP.NET中手动进行压缩并不有趣。 But, if you are as hard-headed as I am, I submit to you the following. 但是,如果您和我一样头脑坚定,我谨向您提出以下几点。

First of all: do NOT use .NET's built-in compression stream classes. 首先:不要使用.NET的内置压缩流类。 They are buggy and can truncate bytes off the end of streams at random. 它们有问题,可以随机截断流末尾的字节。 I've been using DotNetZip with good results: http://dotnetzip.codeplex.com/ 我一直在使用DotNetZip并取得良好的效果: http ://dotnetzip.codeplex.com/

Now, some additional notes: 现在,一些附加说明:

  • Response.TransmitFile() doesn't work with response filtering. Response.TransmitFile()不适用于响应过滤。
  • Response.BinaryWrite() doesn't work with response filtering, so you can't loop over the contents of the file and write it out that way. Response.BinaryWrite()不适用于响应过滤,因此您无法循环遍历文件的内容并将其写出。
  • Response.OutputStream doesn't work with response filtering, so you can't loop over the contents of the file and write it out THAT way, either. Response.OutputStream不能与响应筛选一起使用,因此,您也无法遍历文件的内容并以这种方式将其写出。
  • Response.WriteFile() DOES work with response filtering, but it loads the entire file into memory and keeps it there until the client closes the connection, which doesn't work well for large files. Response.WriteFile()确实可以与响应筛选一起使用,但是它将整个文件加载到内存中并保存在那里,直到客户端关闭连接为止,这不适用于大型文件。
  • And to make things just that little bit more fun: response filtering stops working if you set Response.BufferOutput to false. 为了使事情变得更加有趣:如果将Response.BufferOutput设置为false,则响应过滤将停止工作。 (I just spent literally hours figuring that out) (我只是花了几个小时才弄清楚这一点)

Obviously, there's a LOT of different issues surrounding response filtering and writing to the output stream. 显然,围绕响应过滤和写入输出流存在很多不同的问题。 Using Reflector and lots of experimentation, this is the best ("best" being in terms of working correctly in a variety of scenarios) solution I've found thus far: 通过使用Reflector和大量实验,这是到目前为止我发现的最佳解决方案(“在各种情况下都能正常工作”)。

  1. Write a class that extends Encoding and call it BinaryEncoding. 写一个扩展Encoding的类,并将其称为BinaryEncoding。 Implement all the methods so that they copy characters and bytes correctly, but of course doing the necessary type casts. 实现所有方法,以便它们正确复制字符和字节,但是当然要进行必要的类型转换。

  2. Set Response.ContentEncoding to an instance of BinaryEncoding (you can use the singleton pattern quite successfully for this). 将Response.ContentEncoding设置为BinaryEncoding的实例(您可以非常成功地使用单例模式)。

  3. Open your file with FileStream. 使用FileStream打开文件。

  4. Create a new StreamReader(fileStream, new BinaryEncoding(), false) . 创建一个new StreamReader(fileStream, new BinaryEncoding(), false) That "false" parameter is very important, it stops the StreamReader from eating byte order marks and overriding your BinaryEncoding. 该“ false”参数非常重要,它可以阻止StreamReader吃字节顺序标记并覆盖您的BinaryEncoding。

  5. Allocate a buffer of char[] (I've found that 32KB is a good size). 分配char []的缓冲区(我发现32KB是一个不错的大小)。

Then, in a loop: 然后,在循环中:

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

Until n is 0. 直到n为0。

Be warned: this method results in fairly high CPU usage. 请注意:此方法会导致相当高的CPU使用率。 On a 100 megabit LAN, CPU usage on one core goes to around 40-50% for a single client downloading at 10MB/sec. 在100兆LAN上,单个客户端以10MB /秒的速度下载时,一个内核上的CPU使用率将达到40-50%左右。 I wish I could find a better way... If I had Reflector Pro, I might be able to find one. 我希望我能找到一种更好的方法...如果我拥有Reflector Pro,也许可以找到一个。

If you are using IIS7 or IIS7.5 I would suggest using the HTTP Compression module rather than rolling your own. 如果您使用的是IIS7或IIS7.5,我建议您使用HTTP压缩模块,而不要自己动手使用。 It might help solve the problem. 这可能有助于解决问题。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM