检测浏览器什么时候接收文件下载

我有一个页面,容许用户下载动态生成的文件。 生成须要很长时间,所以我想显示一个“等待”指示。 问题是,我不知道如何检测浏览器什么时候收到文件,所以能够隐藏指示器。 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会下载文件(具备错误的名称和内容类型),而且不会触发“加载”事件。 浏览器

一种不一样的方法多是调用开始文件建立,而后轮询服务器,直到服务器就绪,而后下载已建立的文件。 可是我宁愿避免在服务器上建立临时文件。 缓存

有谁有更好的主意吗? 服务器


#1楼

若是您下载的是已保存的文件,而不是保存在文档中,则没法肯定下载的完成时间,由于它不在当前文档的范围内,而是在浏览器中的单独过程。 app


#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'");
        }
    }
相关文章
相关标签/搜索