我有一个页面,容许用户下载动态生成的文件。 生成须要很长时间,所以我想显示一个“等待”指示。 问题是,我不知道如何检测浏览器什么时候收到文件,所以能够隐藏指示器。 javascript
我正在以隐藏的形式发出请求,该请求会发布到服务器,并以隐藏的iframe做为结果。 这样一来,我就不会用结果替换整个浏览器窗口。 我在iframe上侦听“加载”事件,但愿下载完成后将触发该事件。 php
我随文件返回一个“ Content-Disposition:附件”标头,这将致使浏览器显示“保存”对话框。 可是浏览器不会在iframe中触发“加载”事件。 html
我尝试的一种方法是使用多部分响应。 所以它将发送一个空的HTML文件以及附加的可下载文件。 例如: java
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会下载文件(具备错误的名称和内容类型),而且不会触发“加载”事件。 浏览器
一种不一样的方法多是调用开始文件建立,而后轮询服务器,直到服务器就绪,而后下载已建立的文件。 可是我宁愿避免在服务器上建立临时文件。 缓存
有谁有更好的主意吗? 服务器
若是您下载的是已保存的文件,而不是保存在文档中,则没法肯定下载的完成时间,由于它不在当前文档的范围内,而是在浏览器中的单独过程。 app
当用户触发文件生成时,您只需为该“下载”分配一个惟一的ID,而后将用户发送到每隔几秒钟刷新一次(或使用AJAX检查)的页面。 文件完成后,将其保存在相同的惟一ID下并... 框架
而后,您能够跳过整个iframe / waiting / browserwindow混乱,但有一个很是优雅的解决方案。 测试
若是您不想在服务器上生成和存储文件,是否愿意存储状态,例如文件进行中,文件已完成? 您的“等待”页面可能会轮询服务器以了解文件生成完成的时间。 您可能不肯定浏览器是否已开始下载,但您会有所信心。
我只是有这个彻底相同的问题。 个人解决方案是使用临时文件,由于我已经生成了一堆临时文件。 提交的表格包括:
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上测试。
问题是在生成文件时有一个“等待”指示,而后在下载文件后恢复正常。 我喜欢这样作的方式是使用隐藏的iFrame并挂接框架的onload事件,以便在下载开始时让个人页面知道。 BUT 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'"); } }