浏览器监听文件下载实现

之前在做类似下载功能的时候,很少会考虑那些优化问题。直接一个超链接了事。

然后。。。我遇到了两个坑。

坑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的地址。

原文地址:https://www.cnblogs.com/qianlegeqian/p/4409565.html