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

2022-08-29 22:59:47

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

我正在请求一个隐藏的表单,该表单将POST发送到服务器,并针对其结果的隐藏iframe。这是,所以我不会用结果替换整个浏览器窗口。我在 iframe 上侦听“加载”事件,希望下载完成后会触发该事件。

我随文件返回一个“”标头,这导致浏览器显示“保存”对话框。但是浏览器不会在 iframe 中触发“load”事件。Content-Disposition: attachment

我尝试的一种方法是使用响应。因此,它将发送一个空的HTML文件,以及附加的可下载文件。multi-part

例如:

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

这在火狐中有效;它接收空的 HTML 文件,触发 “load” 事件,然后显示可下载文件的“保存”对话框。但它在Internet ExplorerSafari上失败了。Internet Explorer 会触发“load”事件,但它不会下载该文件,而 Safari 会下载该文件(使用错误的名称和内容类型),并且不会触发 “load” 事件。

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

我该怎么办?


答案 1

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

客户端算法:

  1. 生成随机的唯一令牌。
  2. 提交下载请求,并将令牌包含在 GET/POST 字段中。
  3. 显示“正在等待”指示灯。
  4. 启动一个计时器,每隔一秒左右,寻找一个名为“fileDownloadToken”(或您决定的任何内容)的cookie。
  5. 如果 Cookie 存在,并且其值与令牌匹配,请隐藏“等待”指示器。

服务器算法:

  1. 在请求中查找 GET/POST 字段。
  2. 如果它具有非空值,请删除一个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
    );
}

答案 2

一个非常简单(而且蹩脚)的单行解决方案是使用事件来关闭加载对话框。当然,如果花费的时间太长,并且用户决定执行其他操作(例如阅读电子邮件),则加载对话框将关闭。window.onblur()