我有一个允许用户下载动态生成文件的页面。生成它需要很长时间,所以我想显示一个“等待”指示器。问题是,我不知道如何检测浏览器何时接收到文件,以便我可以隐藏指示器。
我请求一个隐藏的表单,它post到服务器,并以一个隐藏的iframe作为其结果的目标。这样我就不会用结果替换整个浏览器窗口。我监听iframe上的“load”事件,希望它在下载完成时触发。
我将文件返回一个“Content-Disposition: attachment”头,这将导致浏览器显示“Save”对话框。但是浏览器不会在iframe中触发“load”事件。
我尝试过的一种方法是采用多部分回答。所以它会发送一个空的HTML文件,以及附加的可下载文件。
例如:
Content-type: multipart/x-mixed-replace;boundary="abcde"
--abcde
Content-type: text/html
--abcde
Content-type: application/vnd.fdf
Content-Disposition: attachment; filename=foo.fdf
file-content
--abcde
这在Firefox中是有效的;它接收空HTML文件,触发“load”事件,然后显示可下载文件的“Save”对话框。但它在ie和Safari浏览器上无法运行;Internet Explorer触发“load”事件,但它不下载文件,而Safari下载文件(带有错误的名称和内容类型),但不触发“load”事件。
另一种方法可能是调用启动文件创建,轮询服务器直到准备就绪,然后下载已经创建的文件。但我宁愿避免在服务器上创建临时文件。
我该怎么办?
这个解决方案非常简单,但很可靠。并且它可以显示真实的进度消息(并且可以轻松地插入到现有流程中):
处理的脚本(我的问题是:通过HTTP检索文件并将其作为ZIP传递)将状态写入会话。
该状态每秒轮询一次。这就是全部(好吧,这不是。您必须注意很多细节(例如,并发下载),但这是一个开始的好地方;-))。
下载页面:
<a href="download.php?id=1" class="download">DOWNLOAD 1</a>
<a href="download.php?id=2" class="download">DOWNLOAD 2</a>
...
<div id="wait">
Please wait...
<div id="statusmessage"></div>
</div>
<script>
// This is jQuery
$('a.download').each(function()
{
$(this).click(
function() {
$('#statusmessage').html('prepare loading...');
$('#wait').show();
setTimeout('getstatus()', 1000);
}
);
});
});
function getstatus() {
$.ajax({
url: "/getstatus.php",
type: "POST",
dataType: 'json',
success: function(data) {
$('#statusmessage').html(data.message);
if(data.status == "pending")
setTimeout('getstatus()', 1000);
else
$('#wait').hide();
}
});
}
</script>
文件getstatus.php
<?php
session_start();
echo json_encode($_SESSION['downloadstatus']);
?>
文件download.php
<?php
session_start();
$processing = true;
while($processing) {
$_SESSION['downloadstatus'] = array("status" =>"pending", "message" => "Processing".$someinfo);
session_write_close();
$processing = do_what_has_2Bdone();
session_start();
}
$_SESSION['downloadstatus'] = array("status" => "finished", "message" => "Done");
// And spit the generated file to the browser
?>