之前在做類似下載功能的時候,很少會考慮那些優化問題。直接一個超鏈接了事。
然后。。。我遇到了兩個坑。
坑1,A項目中的導出按鈕,測試妹紙在火狐瀏覽器上死活點不出來,而我自己的火狐上是正常的,最后我發現,是迅雷下載插件惹的禍T。T
坑2,B項目中,某運營連續點了10次導出按鈕,然后。。。網站變得很卡。
對於坑1,我們可以用iframe去實現下載。
$('#J-download').on('click',function(){
var iframe=$('<iframe />').attr('src', url).attr('id','iframe_download_report').hide().appendTo('body');
});
而對於坑2,
首先我需要做連續點擊的限制:
利用setTimeout設置一個觸發延時,1秒以內的連續點擊都只算做一次。但是事實上很少會有這樣的惡意點擊,這種控制就很顯得很雞肋。
而最重要的一點就是做到“waiting”效果,點擊下載按鈕后就禁用該按鈕,同時按鈕文本顯示“下載中”,等到瀏覽器開始下載,再啟用該按鈕,重置按鈕文本。
所以問題的關鍵就是如何監聽到文件下載。
最開始的思路想到了iframe的onload事件,iframe加載html頁面,確實會觸發onload事件,但是下載卻不會。又想到了異步下載,也被我否決。第二天開會的時候,我問到這個問題的解決辦法,一位后端提到他們已有解決方案就是使用cookie,豁然開朗。想到之前百度時在stack overflow上被我匆匆一瞥忽略的cookie和token,原來答案很早就有了冏rz
思路大致是這樣:
前端生成一個唯一的token,以get方式隨url傳給后端。后端將token寫進cookie中,而前端通過定時器獲取,然后核對前端生成的token和通過cookie獲取的token值是否一致。
生成唯一的token,這個可以用new Date()結合隨機數做:
_setFormToken:function(){ return ""+(Math.random()*1024|0)+new Date().getTime(); }
禁用啟用鏈接,因為超鏈接不支持disabled屬性,我們可以操作它的href屬性~~
其他的就很簡單了,只要檢測到token一致,就啟用鏈接,然后移除iframe。如果沒有獲取到token或者token不一致,就繼續setTimeout。
_iframeDownLoad:function(){ var timer,_this=this,flag=0; $(document).on('click','.J_DownLog',function(){ var downloadToken=_this._setFormToken(), url=this.href+"&downloadToken="+downloadToken, triggerDelay = 1000, btn=$(this); _this._disableLink(btn,"導出中..."); clearTimeout(timer); timer=setTimeout(function() { function checkToken(){ sertoken=_this._getCookie( "downloadToken" ); if(sertoken==downloadToken){ clearTimeout( downloadTimer ); _this._expireCookie( "downloadToken" ); frame.remove(); _this._enableLink(btn,"導出日志"); flag=0; }else{ checkToken(); } } if(!flag){ flag=1; var frame=$('<iframe />').attr('src', url).attr('id','iframe_download_report').hide().appendTo('body'); var downloadTimer=setTimeout(checkToken,1000); } }, triggerDelay); return false; }); }
-----------------后續------------------
2015.04.28
模擬了下低網速的情況,發現瀏覽器獲取token與彈窗下載對話框之間有時間差,如果每次都是點擊導出時添加新的iframe/導出后移除iframe,就會產生iframe過早移除的情況,這樣下載對話框就彈不出來了。所以,需要修改為第一次生成iframe,之后都是修改iframe的地址。