Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
419 views
in Technique[技术] by (71.8m points)

javascript - 检测浏览器何时接收文件下载(Detect when browser receives file download)

I have a page that allows the user to download a dynamically-generated file.(我有一个页面,允许用户下载动态生成的文件。)

It takes a long time to generate, so I'd like to show a "waiting" indicator.(生成需要很长时间,因此我想显示一个“等待”指示。) The problem is, I can't figure out how to detect when the browser has received the file, so I can hide the indicator.(问题是,我不知道如何检测浏览器何时收到文件,因此可以隐藏指示器。)

I'm making the request in a hidden form, which POSTs to the server, and targets a hidden iframe for its results.(我正在以隐藏的形式发出请求,该请求会发布到服务器,并以隐藏的iframe作为结果。)

This is so I don't replace the entire browser window with the result.(这样一来,我就不会用结果替换整个浏览器窗口。) I listen for a "load" event on the iframe, in the hope that it will fire when the download is complete.(我在iframe上侦听“加载”事件,希望下载完成后将触发该事件。)

I return a "Content-Disposition: attachment" header with the file, which causes the browser to show the "Save" dialog.(我随文件返回一个“ Content-Disposition:附件”标头,这将导致浏览器显示“保存”对话框。)

But the browser doesn't fire a "load" event in the iframe.(但是浏览器不会在iframe中触发“加载”事件。)

One approach I tried is using a multi-part response.(我尝试的一种方法是使用多部分响应。)

So it would send an empty HTML file, as well as the attached downloadable file.(因此它将发送一个空的HTML文件以及附加的可下载文件。) For example:(例如:)
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

This works in Firefox;(这在Firefox中有效;)

it receives the empty HTML file, fires the "load" event, then shows the "Save" dialog for the downloadable file.(它接收到空的HTML文件,触发“加载”事件,然后显示可下载文件的“保存”对话框。) But it fails on IE and Safari;(但是它在IE和Safari上失败;) IE fires the "load" event but doesn't download the file, and Safari downloads the file (with the wrong name and content-type), and doesn't fire the "load" event.(IE会触发“加载”事件,但不会下载文件,而Safari会下载文件(具有错误的名称和内容类型),并且不会触发“加载”事件。)

A different approach might be to make a call to start the file creation, then poll the server until it's ready, then download the already-created file.(一种不同的方法可能是调用开始文件创建,然后轮询服务器,直到服务器就绪,然后下载已创建的文件。)

But I'd rather avoid creating temporary files on the server.(但是我宁愿避免在服务器上创建临时文件。)

Does anyone have a better idea?(有谁有更好的主意吗?)

  ask by JW. translate from so

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

One possible solution uses JavaScript on the client.(一种可能的解决方案是在客户端上使用JavaScript。)

The client algorithm:(客户端算法:)

  1. Generate a random unique token.(生成随机的唯一令牌。)
  2. Submit the download request, and include the token in a GET/POST field.(提交下载请求,并将令牌包含在GET / POST字段中。)
  3. Show the "waiting" indicator.(显示“正在等待”指示器。)
  4. Start a timer, and every second or so, look for a cookie named "fileDownloadToken" (or whatever you decide).(启动一个计时器,然后每隔一秒钟左右,查找一个名为“ fileDownloadToken”(或您决定的内容)的cookie。)
  5. If the cookie exists, and its value matches the token, hide the "waiting" indicator.(如果cookie存在,并且其值与令牌匹配,则隐藏“等待”指示符。)

The server algorithm:(服务器算法:)

  1. Look for the GET/POST field in the request.(在请求中查找GET / POST字段。)
  2. If it has a non-empty value, drop a cookie (eg "fileDownloadToken"), and set its value to the token's value.(如果它具有非空值,则删除一个cookie(例如“ fileDownloadToken”),并将其值设置为令牌的值。)

Client source code (JavaScript):(客户端源代码(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;
}

Example server code (PHP):(服务器代码示例(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();

Where:(哪里:)

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
    );
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...