关于javascript:检测浏览器何时接收文件下载

Detect when browser receives file download

我有一个页面,允许用户下载动态生成的文件。生成时间很长,所以我想显示一个"等待"指示器。问题是,我不知道如何检测浏览器何时收到文件,所以我可以隐藏指示器。

我以一个隐藏的形式提出请求,该表单将发布到服务器,并针对其结果生成一个隐藏的iframe。所以我不会用结果替换整个浏览器窗口。我在iframe上监听一个"加载"事件,希望下载完成后它会启动。

我将返回一个带有文件的"content-disposition:attachment"头文件,这将导致浏览器显示"save"对话框。但是浏览器不会在iFrAME中引发一个"加载"事件。

我尝试的一种方法是使用多部分响应。所以它会发送一个空的HTML文件,以及附加的可下载文件。例如:

1
2
3
4
5
6
7
8
9
10
11
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下载文件(名称和内容类型错误),并且不触发"加载"事件。

另一种方法可能是调用以开始创建文件,然后轮询服务器直到它准备就绪,然后下载已经创建的文件。但我宁愿避免在服务器上创建临时文件。

有人有更好的主意吗?


一种可能的解决方案是在客户机上使用JavaScript。

客户端算法:

  • 生成随机唯一令牌。
  • 提交下载请求,并在get/post字段中包含令牌。
  • 显示"等待"指示灯。
  • 启动一个计时器,每隔一秒左右,寻找一个名为"filedownloadtoken"的cookie(或任何你决定的)。
  • 如果cookie存在,并且其值与令牌匹配,则隐藏"等待"指示器。
  • 服务器算法:

  • 在请求中查找GET/POST字段。
  • 如果它有一个非空值,则删除cookie(例如"FieldWookLoad令牌"),并将其值设置为令牌的值。
  • 客户端源代码(JavaScript):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    function getCookie( name ) {
      var parts = document.cookie.split(name +"=");
      if (parts.length == 2) return parts.pop().split(";").shift();
    }

    function expireCookie( cName ) {
        document.cookie =
            encodeURIComponent(cName) +"=deleted; expires=" + new Date( 0 ).toUTCString();
    }

    function setCursor( docStyle, buttonStyle ) {
        document.getElementById("doc" ).style.cursor = docStyle;
        document.getElementById("button-id" ).style.cursor = buttonStyle;
    }

    function setFormToken() {
        var downloadToken = new Date().getTime();
        document.getElementById("downloadToken" ).value = downloadToken;
        return downloadToken;
    }

    var downloadTimer;
    var attempts = 30;

    // Prevents double-submits by waiting for a cookie from the server.
    function blockResubmit() {
        var downloadToken = setFormToken();
        setCursor("wait","wait" );

        downloadTimer = window.setInterval( function() {
            var token = getCookie("downloadToken" );

            if( (token == downloadToken) || (attempts == 0) ) {
                unblockSubmit();
            }

            attempts--;
        }, 1000 );
    }

    function unblockSubmit() {
      setCursor("auto","pointer" );
      window.clearInterval( downloadTimer );
      expireCookie("downloadToken" );
      attempts = 30;
    }

    示例服务器代码(PHP):

    1
    2
    3
    4
    5
    6
    7
    8
    $TOKEN ="downloadToken";

    // Sets a cookie so that when the download begins the browser can
    // unblock the submit button (thus helping to prevent multiple clicks).
    // The false parameter allows the cookie to be exposed to JavaScript.
    $this->setCookieToken( $TOKEN, $_GET[ $TOKEN ], false );

    $result = $this->sendFile();

    在哪里?

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    public function setCookieToken(
        $cookieName, $cookieValue, $httpOnly = true, $secure = false ) {

        // See: http://stackoverflow.com/a/1459794/59087
        // See: http://shiflett.org/blog/2006/mar/server-name-versus-http-host
        // See: http://stackoverflow.com/a/3290474/59087
        setcookie(
            $cookieName,
            $cookieValue,
            2147483647,            // expires January 1, 2038
           "/",                   // your path
            $_SERVER["HTTP_HOST"], // your domain
            $secure,               // Use true over HTTPS
            $httpOnly              // Set true for $AUTH_COOKIE_NAME
        );
    }


    一个非常简单(也是跛足)的单行解决方案是使用window.onblur()事件关闭加载对话框。当然,如果时间太长,用户决定做其他事情(如阅读电子邮件),加载对话框将关闭。


    旧线,我知道…

    但那些由谷歌领导的人可能对我的解决方案感兴趣。它很简单,但也很可靠。它使显示真正的进度消息成为可能(并且可以很容易地插入到现有进程中):

    处理文件的脚本(我的问题是:通过HTTP检索文件并将其作为zip传递)将状态写入会话。

    每秒轮询和显示状态。仅此而已(好吧,不是。您必须处理很多细节(例如并发下载),但这是一个很好的开始;-)。

    下载页面:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
        DOWNLOAD 1
        DOWNLOAD 2
        ...
       
        Please wait...
       
       
       
    //this is jquery
        $('a.download').each(function()
           {
            $(this).click(
                 function(){
                   $('#statusmessage').html('prepare loading...');
                   $('#wait').show();
                   setTimeout('getstatus()', 1000);
                 }
              );
            });
        });
        function getstatus(){
          $.ajax({
              url:"/getstatus.php",
              type:"POST",
              dataType: 'json',
              success: function(data) {
                $('#statusmessage').html(data.message);
                if(data.status=="pending")
                  setTimeout('getstatus()', 1000);
                else
                  $('#wait').hide();
              }
          });
        }

    GETStasuS.PHP

    1
    2
    3
    4
    <?php
    session_start();
    echo json_encode($_SESSION['downloadstatus']);
    ?>

    下载程序

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
        <?php
        session_start();
        $processing=true;
        while($processing){
          $_SESSION['downloadstatus']=array("status"=>"pending","message"=>"Processing".$someinfo);
          session_write_close();
          $processing=do_what_has_2Bdone();
          session_start();
        }
          $_SESSION['downloadstatus']=array("status"=>"finished","message"=>"Done");
    //and spit the generated file to the browser
        ?>


    我使用下面的内容下载blobs并在下载后撤销对象URL。它在Chrome和Firefox中工作!

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    function download(blob){
        var url = URL.createObjectURL(blob);
        console.log('create ' + url);

        window.addEventListener('focus', window_focus, false);
        function window_focus(){
            window.removeEventListener('focus', window_focus, false);                  
            URL.revokeObjectURL(url);
            console.log('revoke ' + url);
        }
        location.href = url;
    }

    关闭"文件下载"对话框后,窗口将恢复焦点,从而触发焦点事件。


    我编写了一个简单的javascript类,它实现了一种类似于欺负回答中描述的技术。希望对这里的人有用。Github项目称为response-monitor.js

    默认情况下,它使用spin.js作为等待指示器,但它还为实现自定义指示器提供一组回调。

    支持jquery,但不需要。

    显著特征

    • 简单集成
    • 无依赖关系
    • jquery插件(可选)
    • spin.js集成(可选)
    • 监控事件的可配置回调
    • 处理多个同时请求
    • 服务器端错误检测
    • 超时检测
    • 跨浏览器

    示例用法

    HTML

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <!-- the response monitor implementation -->
    <script src="response-monitor.js">

    <!-- optional JQuery plug-in -->
    <script src="response-monitor.jquery.js">

    Link 1 (Timeout: 30s)
    Link 2 (Timeout: 10s)

    <form id="my_form" method="POST">
        <input type="text" name="criteria1">
        <input type="text" name="criteria2">
        <input type="submit" value="Download Report">
    </form>

    客户端(纯javascript)

    1
    2
    3
    4
    5
    6
    7
    //registering multiple anchors at once
    var my_anchors = document.getElementsByClassName('my_anchors');
    ResponseMonitor.register(my_anchors); //clicking on the links initiates monitoring

    //registering a single form
    var my_form = document.getElementById('my_form');
    ResponseMonitor.register(my_form); //the submit event will be intercepted and monitored

    客户端(jQuery)

    1
    2
    $('.my_anchors').ResponseMonitor();
    $('#my_form').ResponseMonitor({timeout: 20});

    带回调的客户端(jquery)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    //when options are defined, the default spin.js integration is bypassed
    var options = {
        onRequest: function(token){
            $('#cookie').html(token);
            $('#outcome').html('');
            $('#duration').html('');
        },
        onMonitor: function(countdown){
            $('#duration').html(countdown);
        },
        onResponse: function(status){
            $('#outcome').html(status==1?'success':'failure');
        },
        onTimeout: function(){
            $('#outcome').html('timeout');
        }
    };

    //monitor all anchors in the document
    $('a').ResponseMonitor(options);

    服务器(PHP)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    $cookiePrefix = 'response-monitor'; //must match the one set on the client options
    $tokenValue = $_GET[$cookiePrefix];
    $cookieName = $cookiePrefix.'_'.$tokenValue; //ex: response-monitor_1419642741528

    //this value is passed to the client through the ResponseMonitor.onResponse callback
    $cookieValue = 1; //for ex,"1" can interpret as success and"0" as failure

    setcookie(
        $cookieName,
        $cookieValue,
        time()+300,            // expire in 5 minutes
       "/",
        $_SERVER["HTTP_HOST"],
        true,
        false
    );

    header('Content-Type: text/plain');
    header("Content-Disposition: attachment; filename="Response.txt"");

    sleep(5); //simulate whatever delays the response
    print_r($_REQUEST); //dump the request in the text file

    有关更多示例,请检查存储库上的Examples文件夹。


    基于埃尔默的例子,我已经准备了自己的解决方案。在元素单击定义的下载类之后,它允许在屏幕上显示自定义消息。我用焦点触发器来隐藏信息。

    JavaScript

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    $(function(){$('.download').click(function() { ShowDownloadMessage(); }); })

    function ShowDownloadMessage()
    {
         $('#message-text').text('your report is creating, please wait...');
         $('#message').show();
         window.addEventListener('focus', HideDownloadMessage, false);
    }

    function HideDownloadMessage(){
        window.removeEventListener('focus', HideDownloadMessage, false);                  
        $('#message').hide();
    }

    HTML

    1
        please wait...

    现在,您应该实现下载的任何元素:

    1
    Download report

    1
    <input class="download" type="submit" value="Download" name="actionType">

    每次下载后,您将看到您的报告正在创建的消息,请稍候…


    如果您正在动态地生成一个文件,并且还实现了一个实时服务器到客户端消息库,那么您可以很容易地提醒您的客户端。

    我喜欢和推荐的服务器到客户端消息库是SoCKE.IO(通过节点.js)。在服务器脚本完成后,生成正在下载的文件,该脚本中的最后一行可以向SoCKET.IO发送消息,它向客户端发送通知。在客户端上,SoCKE.IO监听从服务器发出的传入消息,并允许您对它们进行操作。使用这种方法比其他方法的好处是,在流媒体完成之后,你能够检测到一个"真实"的完成事件。

    例如,您可以在单击下载链接后显示忙指示器,传输文件,在流脚本的最后一行从服务器向socket.io发送消息,在客户端上侦听通知,接收通知并通过隐藏忙指示器来更新您的UI。

    我意识到大多数阅读这个问题的答案的人可能没有这种类型的设置,但我已经在我自己的项目中使用了这种精确的解决方案,效果非常好。

    socket.io非常容易安装和使用。更多信息:http://socket.io/


    我参加晚会很晚,但如果有人想知道我的解决方案,我就把它挂在这里:

    我确实遇到了这个问题,但我找到了一个可行的解决方案。这很可怕,但它对我的一个简单问题起作用。

    我有一个HTML页面,它启动了一个单独的PHP脚本,生成了这个文件,然后下载了它。在HTML页面上,我在HTML标题中使用了以下jquery(您还需要包括jquery库):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
        $(function(){
            var iframe = $("<iframe>", {name: 'iframe', id: 'iframe',}).appendTo("body").hide();
            $('#click').on('click', function(){
                $('#iframe').attr('src', 'your_download_script.php');
            });
            $('iframe').load(function(){
                $('#iframe').attr('src', 'your_download_script.php?download=yes'); <!--on first iframe load, run script again but download file instead-->
                $('#iframe').unbind(); <!--unbinds the iframe. Helps prevent against infinite recursion if the script returns valid html (such as echoing out exceptions) -->
            });
        });

    在您的_download_script.php上,有以下内容:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    function downloadFile($file_path) {
        if (file_exists($file_path)) {
            header('Content-Description: File Transfer');
            header('Content-Type: text/csv');
            header('Content-Disposition: attachment; filename=' . basename($file_path));
            header('Expires: 0');
            header('Cache-Control: must-revalidate');
            header('Pragma: public');
            header('Content-Length: ' . filesize($file_path));
            ob_clean();
            flush();
            readfile($file_path);
            exit();
        }
    }


    $_SESSION['your_file'] = path_to_file; //this is just how I chose to store the filepath

    if (isset($_REQUEST['download']) && $_REQUEST['download'] == 'yes') {
        downloadFile($_SESSION['your_file']);
    } else {
        *execute logic to create the file*
    }

    为了解决这个问题,jquery首先在iframe中启动PHP脚本。生成文件后将加载iframe。然后jquery用一个请求变量再次启动脚本,告诉脚本下载文件。

    不能一次完成下载和文件生成的原因是php header()函数。如果使用header(),则将脚本更改为网页以外的内容,jquery将永远不会将下载脚本识别为"已加载"。我知道浏览器接收到文件时可能不一定会检测到这一点,但您的问题听起来与我的类似。


    如果您不想在服务器上生成和存储文件,您是否愿意存储状态,例如"文件正在进行"、"文件完成"?您的"等待"页面可以轮询服务器以了解文件生成何时完成。你不知道浏览器是否启动了下载,但你还是有信心的。


    当用户触发文件的生成时,您只需为该"下载"分配一个唯一的ID,并将用户发送到一个每隔几秒钟刷新(或使用Ajax检查)一次的页面。完成文件后,将其保存在同一唯一ID下,然后…

    • 如果文件已准备好,请进行下载。
    • 如果文件尚未准备好,请显示进度。

    然后,您可以跳过整个iframe/waiting/browserwindow混乱,但有一个真正优雅的解决方案。


    我也有同样的问题。我的解决方案是使用临时文件,因为我已经生成了许多临时文件。该表格提交时附带:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    var microBox = {
        show : function(content) {
            $(document.body).append('' +
            content + '');
            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...');

    在生成文件的脚本末尾,我有:

    1
    2
    header('Refresh: 0;url=fetch.php?token=' . $token);
    echo '<html></html>';

    这将导致触发iframe上的加载事件。然后,等待消息关闭,然后开始文件下载。在IE7和火狐上测试过。


    "如何检测浏览器何时收到文件下载?"
    我在配置时遇到了同样的问题:
    支柱1.2.9
    jquery-1.3.2.
    jquery-ui-1.7.1.自定义I.BR/> IE 11Java 5


    我的cookie解决方案:
    -客户端:
    提交表单时,调用javascript函数隐藏页面并加载等待微调器

    1
    2
    3
    function loadWaitingSpinner(){
    ... hide your page and show your spinner ...
    }

    然后,调用一个函数,该函数每隔500毫秒检查一次cookie是否来自服务器。

    1
    2
    3
    function checkCookie(){
        var verif = setInterval(isWaitingCookie,500,verif);
    }

    如果找到cookie,请停止每隔500毫秒检查一次,使cookie过期,并调用函数返回页面并删除等待微调器(removeWaitingsPinner())。如果您想再次下载另一个文件,则必须使cookie过期!

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    function isWaitingCookie(verif){
        var loadState = getCookie("waitingCookie");
        if (loadState =="done"){
            clearInterval(verif);
            document.cookie ="attenteCookie=done; expires=Tue, 31 Dec 1985 21:00:00 UTC;";
            removeWaitingSpinner();
        }
    }
        function getCookie(cookieName){
            var name = cookieName +"=";
            var cookies = document.cookie
            var cs = cookies.split(';');
            for (var i = 0; i < cs.length; i++){
                var c = cs[i];
                while(c.charAt(0) == ' ') {
                    c = c.substring(1);
                }
                if (c.indexOf(name) == 0){
                    return c.substring(name.length, c.length);
                }
            }
            return"";
        }
    function removeWaitingSpinner(){
    ... come back to your page and remove your spinner ...
    }

    -服务器端:
    在服务器进程结束时,向响应中添加一个cookie。当您的文件准备好下载时,该cookie将被发送到客户机。

    1
    2
    Cookie waitCookie = new Cookie("waitingCookie","done");
    response.addCookie(waitCookie);

    我希望能帮助别人!


    如果您下载了一个已保存的文件,而不是文档中的文件,则无法确定下载完成的时间,因为它不在当前文档的范围内,而是浏览器中的一个单独进程。


    如果您只想在显示下载对话框之前显示消息或加载程序gif,那么一个快速的解决方案是将消息放入隐藏的容器中,当您单击生成要下载的文件的按钮时,可以使容器可见。然后使用jquery或javascript捕获按钮的focusout事件以隐藏包含消息的容器


    如果xmlhttprequest with blob不是一个选项,那么您可以在新窗口中打开您的文件,并检查eny元素是否以间隔填充到该窗口体中。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    var form = document.getElementById("frmDownlaod");
     form.setAttribute("action","downoad/url");
     form.setAttribute("target","downlaod");
     var exportwindow = window.open("","downlaod","width=800,height=600,resizable=yes");
     form.submit();

    var responseInterval = setInterval(function(){
        var winBody = exportwindow.document.body
        if(winBody.hasChildNodes()) // or 'downoad/url' === exportwindow.document.location.href
        {
            clearInterval(responseInterval);
            // do your work
            // if there is error page configured your application for failed requests, check for those dom elemets
        }
    }, 1000)
    //Better if you specify maximun no of intervals


    问题是在生成文件时要有一个"等待"指示器,然后在文件下载后恢复正常。我喜欢这样做的方式是使用隐藏的iframe并钩住框架的onload事件,以便在下载开始时让我的页面知道。但是,在IE中,对于文件下载,onload不会触发(就像使用附件头标记一样)。轮询服务器可以工作,但我不喜欢这种额外的复杂性。所以我要做的是:

    • 像往常一样瞄准隐藏的iframe。
    • 生成内容。缓存它绝对超时2分钟。
    • 发送一个javascript重定向回调用客户机,本质上调用第二次生成页面。注意:这将导致在IE中触发OnLoad事件,因为它的行为类似于常规页面。
    • 从缓存中删除内容并发送给客户。

    免责声明,不要在繁忙的网站上这样做,因为缓存可能会累积。但实际上,如果您的站点忙于长时间运行的进程,无论如何都会使线程匮乏。

    下面是代码隐藏的样子,这是您真正需要的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    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'");
            }
        }


    单击按钮/链接时创建iframe并将其附加到正文。

    1
    2
    3
    4
    5
                      $('<iframe />')
                     .attr('src', url)
                     .attr('id','iframe_download_report')
                     .hide()
                     .appendTo('body');

    延迟创建iframe并在下载后将其删除。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
                                var triggerDelay =   100;
                                var cleaningDelay =  20000;
                                var that = this;
                                setTimeout(function() {
                                    var frame = $('<iframe style="width:1px; height:1px;" class="multi-download-frame"></iframe>');
                                    frame.attr('src', url+"?"+"Content-Disposition: attachment ; filename="+that.model.get('fileName'));
                                    $(ev.target).after(frame);
                                    setTimeout(function() {
                                        frame.remove();
                                    }, cleaningDelay);
                                }, triggerDelay);