檢測瀏覽器什麼時候接收文件下載

我有一個頁面,容許用戶下載動態生成的文件。 生成須要很長時間,所以我想顯示一個「等待」指示。 問題是,我不知道如何檢測瀏覽器什麼時候收到文件,所以能夠隱藏指示器。 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'");
        }
    }
相關文章
相關標籤/搜索