【发布时间】:2011-12-20 22:10:16
【问题描述】:
我们的 Web 应用程序(ASP.NET Web 表单)有一个页面,该页面将向用户显示最近生成的 PDF 文件。由于 PDF 文件有时非常大,我们实施了一种“流式传输”方法,将其分块发送到客户端浏览器。
尽管以块的形式向下发送数据,但我们在发送文件之前就知道文件的完整大小,因此我们适当地设置了 Content-Length 标头。直到今天,这已经在我们的生产环境中运行了一段时间(并且在我们的测试环境中继续使用几乎相同的配置)。报告的问题是 Chrome 会尝试打开 PDF 文件,但会因“加载”动画卡住而挂起。
因为在我们的测试环境中一切正常,所以我能够使用 Firebug 查看在两个环境中返回的响应标头。在测试环境中,我看到了一个正确的“Content-Length”标头,而在生产环境中,它已被替换为 Transfer-Encoding: chunked 标头。 Chrome 不喜欢这样,因此挂断了。
我已经阅读了一些文章和帖子,这些文章和帖子讨论了在没有提供 Content-Length 标头时如何显示 Transfer-Encoding 标头,但是我们指定了 Content-Length 标头,并且在运行相同的标头时一切似乎仍然有效测试服务器上相同 PDF 文件的代码。
测试和生产服务器都运行 IIS 7.5,并且都启用了动态和静态压缩。
这里是有问题的代码:
var fileInfo = new FileInfo(fileToSendDown);
Response.ClearHeaders();
Response.ContentType = "application/pdf";
Response.AddHeader("Content-Disposition", "filename=test.pdf");
Response.AddHeader("Content-Length", fileInfo.Length.ToString());
var buffer = new byte[1024];
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
{
int read;
while ((read = fs.Read(buffer, 0, 1024)) > 0)
{
if (!response.IsClientConnected) break;
Response.OutputStream.Write(buffer, 0, read);
Response.Flush();
}
}
我很幸运地在我的本地工作站上看到了相同的行为,因此使用调试器我能够看到“Transfer-Encoding: chunked”标头在调用期间通过 while 循环的第二次设置'冲洗'。此时,响应同时具有 Content-Length 标头和 Transfer-Encoding 标头,但不知何故,当响应到达浏览器时,Firebug 仅显示 Transfer-Encoding 标头。
更新
我想我已经跟踪到使用“块”中发送数据并将“过滤器”附加到 HttpResponse 对象的组合(我们使用过滤器来跟踪被发送到的视图状态的大小)每页)。在将 PDF 发送到浏览器时,我们使用 HTTP 过滤器没有任何意义,因此在此处清除过滤器已经解决了我们的问题。纯粹出于好奇,我决定深入挖掘一下,如果其他人将来偶然发现这个问题,我会更新这个问题。
我在 AppHarbor 上有一个简单的应用程序可以重现该问题:http://transferencodingtest.apphb.com/。如果您同时选中“使用过滤器?”和“发送块?”框,您应该能够看到“传输编码:分块”标题出现(使用 Chrome 开发工具、Firebug、Fiddler 等)。如果未选中其中任何一个框,您将获得正确的内容长度标头。底层代码在 github 上,所以你可以看到幕后发生的事情:
https://github.com/appakz/TransferEncodingTest
请注意,要在本地复制,您需要在 IIS 7.5 中设置本地网站(7 也可以工作,我没有尝试过)。 Visual Studio 附带的 ASP .NET 开发服务器不会重现该问题。
我在此处的博客文章中添加了更多详细信息:'Content-Length' Header Replaced With 'Transfer-Encoding: Chunked' in ASP .NET
【问题讨论】:
-
我知道这是一个老问题,但是在做类似的研究时,我遇到了this excellent post 解释这个问题。产生这个问题需要一组特定的环境。希望这对未来的读者有所帮助。
-
那是我在提出问题后写的帖子,没有得到答案,然后终于找出了一些步骤来持续重现它。我用复制它的示例应用程序的链接更新了原始问题,但我想我也应该添加一个指向博客文章的链接。