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