我们的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();
}
}
Run Code Online (Sandbox Code Playgroud)
我很幸运能够在我的本地工作站上看到相同的行为,所以使用调试器我已经能够看到'Transfer-Encoding:chunked'标头是在调用'Flush'期间第二次通过while循环时设置的.此时,响应具有Content-Length头和Transfer-Encoding头,但是当响应到达浏览器时,Firebug仅以某种方式显示Transfer-Encoding头.
UPDATE
我想我已经跟踪了这一点,结合使用"块"发送数据并将"过滤器"附加到HttpResponse对象(我们使用过滤器来跟踪向下发送到每个页面的视图状态的大小) .我们在向浏览器发送PDF时使用HTTP过滤器没有任何意义,因此清除此处的过滤器已解决了我们的问题.我决定纯粹出于好奇心深入挖掘并更新了这个问题,如果其他人在将来偶然发现这个问题.
我在AppHarbor上有一个简单的应用程序可以重现这个问题:http://transferencodingtest.apphb.com/ .如果同时选中"使用过滤器?" 和'发送大块?' 您应该能够看到"transfer-encoding:chunked"标题显示的框(使用Chrome开发工具,Firebug,Fiddler等).如果未选中其中任何一个框,您将获得正确的内容长度标题.基础代码在github上运行,因此您可以看到幕后发生的事情:
https://github.com/appakz/TransferEncodingTest
请注意,要在本地重新登录,您需要在IIS 7.5中设置本地网站(7也可以使用,我还没试过).Visual Studio附带的ASP .NET开发服务器不会重现问题.
我在博客文章中添加了一些更多细节:'Content-Length'标题替换为ASP .NET中的'Transfer-Encoding:Chunked'
从MSDN上的一篇文章看来,您可以禁用分块编码:
appcmd set config /section:asp /enableChunkedEncoding:False
Run Code Online (Sandbox Code Playgroud)
但这是在ASP settings下提到的,因此它可能不适用于从ASP.NET处理程序生成的响应。