XMLHttpRequest 2级 - 判断上传是否完成

21

我正在使用ajax进行文件上传。文件上传完成后,php应该检查它的mime、大小、病毒(clamscan)等,对于较大的文件可能需要几秒钟的时间。在上传文件时,HTML5的<progress>会填充进度条,当文件准备好并且PHP开始检查时,进度条应该切换到不确定状态。我想过两种方法来实现这一点(它们都起作用):

检查upload.onload事件

xhr.upload.addEventListener("load", function (e) {
    $("#uploadprogress").attr("value", false);
    $("#uploadprogress").attr("max", false);
    $("#progress").text("Checking file...");
});

这不起作用,因为onload事件在请求准备就绪时触发,而不是上传完成时触发。

检查上传进度百分比是否为100%

xhr.upload.addEventListener("progress", function (e) {
    if (e.lengthComputable && e) {
        p = (e.loaded / e.total);
        if (p==1) {
            $("#uploadprogress").attr("value", false);
            $("#uploadprogress").attr("max", false);
            $("#progress").text("Checking file...");
        } else {
            var percent = Math.ceil(p * 1000) / 10;
            $("#uploadprogress").val(e.loaded);
            $("#uploadprogress").attr("max", e.total);
            $("#progress").text("Uploading... " + percent + "%");
        }
   }
}
});

由于上传百分比有时会在约97%停止,尽管上传已完成,并且PHP开始处理文件,因此这不起作用。

还有其他检查的可能性吗?

4个回答

60
您想监听的事件是XHR对象上的readystatechange(而不是XHR.upload)。当上传完成发送并且服务器关闭连接时,readyState4。无论服务器是否关闭连接,loadend/load在上传完成后都会触发。仅供参考,以下是您可以监听的事件及其触发时间:
    var xhr = new XMLHttpRequest();

    // ...
    // do stuff with xhr
    // ...

    xhr.upload.addEventListener('loadstart', function(e) {
      // When the request starts.
    });
    xhr.upload.addEventListener('progress', function(e) {
      // While sending and loading data.
    });
    xhr.upload.addEventListener('load', function(e) {
      // When the request has *successfully* completed.
      // Even if the server hasn't responded that it finished.
    });
    xhr.upload.addEventListener('loadend', function(e) {
      // When the request has completed (either in success or failure).
      // Just like 'load', even if the server hasn't 
      // responded that it finished processing the request.
    });
    xhr.upload.addEventListener('error', function(e) {
      // When the request has failed.
    });
    xhr.upload.addEventListener('abort', function(e) {
      // When the request has been aborted. 
      // For instance, by invoking the abort() method.
    });
    xhr.upload.addEventListener('timeout', function(e) {
      // When the author specified timeout has passed 
      // before the request could complete.
    });

    // notice that the event handler is on xhr and not xhr.upload
    xhr.addEventListener('readystatechange', function(e) {
      if( this.readyState === 4 ) {
        // the transfer has completed and the server closed the connection.
      }
    });

2
但我不想检查连接是否关闭,因为上传完成后,PHP会检查文件,这需要大约10秒钟,并返回一些JSON。当PHP开始检查文件(=上传完成时),进度条应该切换到“不确定”。 - ninov
1
当文件传输完成时,loadend/load事件会触发,当您的脚本完成对文件的检查(也就是PHP关闭连接时),readystatechange事件会触发,其readyState===4。因此,为了您的目的,在readystatechange事件中将进度条切换为“不确定状态”。请参见:https://gist.github.com/anonymous/5208652 - zertosh
不起作用。这是我的JS代码:https://gist.github.com/anonymous/2a2fef447399a3b8e734 假设PHP脚本只是sleep(10);。现在我想为这10秒显示一个"不确定"的进度条。 - ninov
2
你可能遇到了浏览器问题。我刚在Chrome 25.0.1364.172和27.0.1449.0上进行了一些语法修正等测试,结果符合预期。然而,FF 19.0.2会同时触发load/loadend/readyState 4 - zertosh
好的,我正在使用Firefox 19.0.2浏览器...我将测试Chrome。 - ninov
2
请确保将监听器添加到上传属性而不是直接添加到xhr。 - Gleno

3

基于https://bugzilla.mozilla.org/show_bug.cgi?id=637002

让我们来看一个完整的工作示例...

// YOUR (SIMPLE) JAVASCRIPT FILE
var form = new FormData(), xhr = new XMLHttpRequest();
form.append('inputname', YOURFILE);

xhr.open('POST', 'http://oneserver/onephpfile', true);
xhr.setRequestHeader('X-CSRF-Token', 'somestring');
xhr.onreadystatechange = function () {
    if ((xhr.readyState === 4) && (xhr.status === 200))
        // do other thing with xhr.responseText.trim()
};

xhr.upload.addEventListener('loadstart', showProgressBarFunction, false);
xhr.upload.addEventListener('progress',  updateProgressBarFunction, false);
xhr.upload.addEventListener('load',      updateProgressBarFunction, false);
xhr.send(form);

// YOUR FIRST (SIMPLE) PHP FILE
header('Content-Type: text/plain; charset=utf-8');
header('Cache-Control: no-cache, must-revalidate');

sleep(20);
echo 'file processing ended';

使用这个第一个PHP文件,你将会看到:在Firefox(4/10/28/32)和IE(10/11)中,进度条会显示:10%... 50%... 75%... 'do other thing'。然而,在Chrome/Chromium(33/37)和Opera(24)中,你将会看到:10%... 50%... 75%... 100%... 'do other thing'

// YOUR SECOND (SIMPLE) PHP FILE
header('Content-Encoding: chunked', true);
header('Content-Type: text/plain; charset=utf-8');
header('Cache-Control: no-cache, must-revalidate');
ini_set('output_buffering', false);
ini_set('implicit_flush', true);
ob_implicit_flush(true);
for ($i = 0; $i < ob_get_level(); $i++)
    ob_end_clean();
echo ' ';

sleep(20);
echo 'file processing ended';

使用第二个PHP文件,您将看到:在Chrome / Chromium(33/37/53),Opera(24/42),Firefox(4/10/28/32/45),IE(10/11)和Edge(14)中,您将看到“ 10%... 50%... 75%... 100%... '做其他事情'”!

1
这是hTML5规范的一个比较明显的缺陷,他们本可以轻松扩展它以添加诸如timeRemaining和transferSpeed等信息。
您是否考虑过使用math.round而不是math.ceil来进行var percent的计算,以便在几个百分点偏差时帮助解决问题?
如果您的UI在后端已经完成但仍然停留在<100%,则还应添加另一个loadComplete监听器。
//only fires once
xhr.addEventListener('loadend', uploadComplete, false);
function uploadComplete(event) {
    console.log('rejoice...for I have completed');
    //do stuff
}

0

检查 readyState,if(readyState==4) {//已完成,在此处放置代码}


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接