我有一个页面,允许用户下载动态生成的文件。 生成需要很长时间,因此我想显示一个“等待”指示。 问题是,我不知道如何检测浏览器何时收到文件,因此可以隐藏指示器。
我正在以隐藏的形式发出请求,该请求会发布到服务器,并以隐藏的iframe作为结果。 这样一来,我就不会用结果替换整个浏览器窗口。 我在iframe上侦听“加载”事件,希望下载完成后将触发该事件。
我随文件返回一个“ Content-Disposition:附件”标头,这将导致浏览器显示“保存”对话框。 但是浏览器不会在iframe中触发“加载”事件。
我尝试的一种方法是使用多部分响应。 因此它将发送一个空的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文件,触发“加载”事件,然后显示可下载文件的“保存”对话框。 但是它在IE和Safari上失败; IE会触发“加载”事件,但不会下载文件,而Safari会下载文件(具有错误的名称和内容类型),并且不会触发“加载”事件。
一种不同的方法可能是调用开始文件创建,然后轮询服务器,直到服务器就绪,然后下载已创建的文件。 但是我宁愿避免在服务器上创建临时文件。
有谁有更好的主意吗?
#1楼
如果您下载的是已保存的文件,而不是保存在文档中,则无法确定下载的完成时间,因为它不在当前文档的范围内,而是在浏览器中的单独过程。
#2楼
当用户触发文件生成时,您只需为该“下载”分配一个唯一的ID,然后将用户发送到每隔几秒钟刷新一次(或使用AJAX检查)的页面。 文件完成后,将其保存在相同的唯一ID下并...
- 如果文件已准备好,请进行下载。
- 如果文件尚未准备好,请显示进度。
然后,您可以跳过整个iframe / waiting / browserwindow混乱,但有一个非常优雅的解决方案。
#3楼
如果您不想在服务器上生成和存储文件,是否愿意存储状态,例如文件进行中,文件已完成? 您的“等待”页面可能会轮询服务器以了解文件生成完成的时间。 您可能不确定浏览器是否已开始下载,但您会有所信心。
#4楼
我只是有这个完全相同的问题。 我的解决方案是使用临时文件,因为我已经生成了一堆临时文件。 提交的表格包括:
var microBox = {
show : function(content) {
$(document.body).append('<div id="microBox_overlay"></div><div id="microBox_window"><div id="microBox_frame"><div id="microBox">' +
content + '</div></div></div>');
return $('#microBox_overlay');
},
close : function() {
$('#microBox_overlay').remove();
$('#microBox_window').remove();
}
};
$.fn.bgForm = function(content, callback) {
// Create an iframe as target of form submit
var id = 'bgForm' + (new Date().getTime());
var $iframe = $('<iframe id="' + id + '" name="' + id + '" style="display: none;" src="about:blank"></iframe>')
.appendTo(document.body);
var $form = this;
// Submittal to an iframe target prevents page refresh
$form.attr('target', id);
// The first load event is called when about:blank is loaded
$iframe.one('load', function() {
// Attach listener to load events that occur after successful form submittal
$iframe.load(function() {
microBox.close();
if (typeof(callback) == 'function') {
var iframe = $iframe[0];
var doc = iframe.contentWindow.document;
var data = doc.body.innerHTML;
callback(data);
}
});
});
this.submit(function() {
microBox.show(content);
});
return this;
};
$('#myForm').bgForm('Please wait...');
在生成文件的脚本末尾,我有:
header('Refresh: 0;url=fetch.php?token=' . $token);
echo '<html></html>';
这将导致触发iframe上的加载事件。 然后关闭等待消息,然后将开始文件下载。 在IE7和Firefox上测试。
#5楼
问题是在生成文件时有一个“等待”指示,然后在下载文件后恢复正常。 我喜欢这样做的方式是使用隐藏的iFrame并挂接框架的onload事件,以便在下载开始时让我的页面知道。 BUT onload不会在IE中触发以进行文件下载(例如带有附件标头令牌)。 轮询服务器可以工作,但是我不喜欢这种额外的复杂性。 所以这是我的工作:
- 像往常一样定位隐藏的iFrame。
- 生成内容。 在2分钟内以绝对超时对其进行缓存。
- 将javascript重定向发送回调用方客户端,实际上是第二次调用生成器页面。 注意:这将导致onload事件在IE中触发,因为它的作用类似于常规页面。
- 从缓存中删除内容并将其发送给客户端。
免责声明,请勿在繁忙的站点上执行此操作,因为可能会增加缓存。 但是,实际上,如果您的站点长时间运行很忙,无论如何都会使您的线程饿死。
这是背后代码的样子,这是您真正需要的。
public partial class Download : System.Web.UI.Page
{
protected System.Web.UI.HtmlControls.HtmlControl Body;
protected void Page_Load( object sender, EventArgs e )
{
byte[ ] data;
string reportKey = Session.SessionID + "_Report";
// Check is this page request to generate the content
// or return the content (data query string defined)
if ( Request.QueryString[ "data" ] != null )
{
// Get the data and remove the cache
data = Cache[ reportKey ] as byte[ ];
Cache.Remove( reportKey );
if ( data == null )
// send the user some information
Response.Write( "Javascript to tell user there was a problem." );
else
{
Response.CacheControl = "no-cache";
Response.AppendHeader( "Pragma", "no-cache" );
Response.Buffer = true;
Response.AppendHeader( "content-disposition", "attachment; filename=Report.pdf" );
Response.AppendHeader( "content-size", data.Length.ToString( ) );
Response.BinaryWrite( data );
}
Response.End();
}
else
{
// Generate the data here. I am loading a file just for an example
using ( System.IO.FileStream stream = new System.IO.FileStream( @"C:\1.pdf", System.IO.FileMode.Open ) )
using ( System.IO.BinaryReader reader = new System.IO.BinaryReader( stream ) )
{
data = new byte[ reader.BaseStream.Length ];
reader.Read( data, 0, data.Length );
}
// Store the content for retrieval
Cache.Insert( reportKey, data, null, DateTime.Now.AddMinutes( 5 ), TimeSpan.Zero );
// This is the key bit that tells the frame to reload this page
// and start downloading the content. NOTE: Url has a query string
// value, so that the content isn't generated again.
Body.Attributes.Add("onload", "window.location = 'binary.aspx?data=t'");
}
}
来源:oschina
链接:https://my.oschina.net/u/3797416/blog/3161148