浏览器收到文件下载时进行检测

我有一个页面允许用户下载一个动态生成的文件。 生成需要很长时间,所以我想显示一个“等待”指标。 问题是,我无法弄清楚如何检测浏览器何时收到文件,所以我可以隐藏指示器。

我以隐藏的形式发出请求,发送到服务器,并针对其结果隐藏iframe。 这是因为我不会用结果替换整个浏览器窗口。 我在iframe上侦听一个“加载”事件,希望它在下载完成时触发。

我用该文件返回一个“Content-Disposition:attachment”标题,这会导致浏览器显示“保存”对话框。 但浏览器不会触发iframe中的“加载”事件。

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

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(例如“fileDownloadToken”),并将其值设置为该令牌的值。

  • 客户端源代码(JavaScript):

    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):

    $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();
    

    哪里:

    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文件传递)将状态写入会话。

    状态每秒轮询并显示。 多数民众赞成在所有(好吧,不是。你必须处理很多细节[例如并发下载],但它是一个开始的好地方;-))。

    下载页面:

        <a href="download.php?id=1" class="download">DOWNLOAD 1</a>
        <a href="download.php?id=2" class="download">DOWNLOAD 2</a>
        ...
        <div id="wait">
        Please wait...
        <div id="statusmessage"></div>
        </div>
        <script>
    //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();
              }
          });
        }
        </script>
    

    getstatus.php

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

    的download.php

        <?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
        ?>
    
    链接地址: http://www.djcxy.com/p/22223.html

    上一篇: Detect when browser receives file download

    下一篇: Part Form to Bamboo API