如何从XMLHttpRequest获取进度

143

是否有可能获取XMLHttpRequest的进度(已上传字节数,已下载字节数)?

当用户上传大文件时,这将非常有用,可以显示一个进度条。标准API似乎不支持它,但是也许有一些浏览器中的非标准扩展?毕竟,客户端知道上传/下载了多少字节,这似乎是一个非常明显的功能。

注:我知道“轮询服务器以获取进度”的替代方案(这就是我现在正在做的)。除了服务器端代码比较复杂之外,主要问题在于通常在上传大文件时,用户的连接完全崩溃,因为大多数ISP提供的上行速度很差。因此,进行额外的请求并不像我希望的那样响应迅速。我希望有一种方法(可能是非标准的),可以随时获取浏览器拥有的此信息。

7个回答

143
对于上传的字节,很容易实现。只需监听 xhr.upload.onprogress 事件即可。浏览器知道要上传的文件大小和已上传数据的大小,因此可以提供进度信息。
对于下载的字节(使用 xhr.responseText 获取信息时),有点更加困难,因为浏览器不知道服务器请求中将发送多少字节。在这种情况下,浏览器所知道的唯一事情是它正在接收的字节数。
有一个解决方法,就是在服务器脚本中设置 Content-Length 标头,以获取浏览器将要接收的字节的总大小。
更多内容请参见:https://developer.mozilla.org/en/Using_XMLHttpRequest
示例:我的服务器脚本读取一个 zip 文件(需要 5 秒钟)。
$filesize=filesize('test.zip');

header("Content-Length: " . $filesize); // set header length
// if the headers is not set then the evt.loaded will be 0
readfile('test.zip');
exit 0;

现在我可以监控服务器脚本的下载进程,因为我知道它的总长度:

function updateProgress(evt) 
{
   if (evt.lengthComputable) 
   {  // evt.loaded the bytes the browser received
      // evt.total the total bytes set by the header
      // jQuery UI progress bar to show the progress on screen
     var percentComplete = (evt.loaded / evt.total) * 100;  
     $('#progressbar').progressbar( "option", "value", percentComplete );
   } 
}   
function sendreq(evt) 
{  
    var req = new XMLHttpRequest(); 
    $('#progressbar').progressbar();    
    req.onprogress = updateProgress;
    req.open('GET', 'test.php', true);  
    req.onreadystatechange = function (aEvt) {  
        if (req.readyState == 4) 
        {  
             //run any callback here
        }  
    };  
    req.send(); 
}

34
值得注意的是,“Content-Length”不是一个估计长度,它必须是精确的长度。如果太短,浏览器将会中断下载;如果太长,浏览器将会认为下载未完成。 - Chris Chilvers
@ChrisChilvers 这意味着一个 PHP 文件可能无法被正确计算,对吗? - user5066707
1
在该示例中,@nicematt来自文件,这是可以的。但是,如果你直接从内存流传输zip文件,则不能随意估计或指定内容长度。 - Chris Chilvers
3
当您访问一个.php页面时,服务器会运行PHP文件并向您发送其输出。服务器应该发送输出的长度,而不是.php文件的长度。 - leewz
@Zac 这只是jQueryUi进度条,用于在用户界面上显示从0%到100%的进度。 - albanx
显示剩余2条评论

11

Firefox支持XHR下载进度事件

编辑2021-07-08 10:30 PDT

上面的链接已经失效。在Mozilla WebDev网站上搜索后找到了以下链接:

https://developer.mozilla.org/zh-CN/docs/Web/API/ProgressEvent

它介绍了如何使用XMLHttpRequest的进度事件,并提供了一个示例。我在下面包含了这个示例:

var progressBar = document.getElementById("p"),
    client = new XMLHttpRequest()
client.open("GET", "magical-unicorns")
client.onprogress = function(pe) {
  if(pe.lengthComputable) {
    progressBar.max = pe.total
    progressBar.value = pe.loaded
  }
}
client.onloadend = function(pe) {
  progressBar.value = pe.loaded
}
client.send()

我也找到了这个链接,我认为这就是原始链接所指向的内容。

https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/progress_event


1
链接已经失效,而且回答没有提供任何其他信息。 - tforgione
@tforgione 这不是我的答案。但是,我刚刚修复了它。享受吧。 - Daniel Rudy
这是一个 JsFiddle,我试图使用 XMHttpRequest 请求 JQuery 库。https://jsfiddle.net/thoakun/7tjnbd8p/。`event.lengthComputable` 未定义,因此无法计算加载进度。 - bytrangle

6

最有前途的方法之一似乎是打开第二个通信渠道,向服务器询问传输已完成的百分比。


4
对于已上传的总大小似乎没有处理的方法,但对于下载,有类似于您想要的东西。一旦readyState为3,您可以定期查询responseText以获取到目前为止下载的所有内容作为字符串(在IE中不起作用),直到所有内容都可用,此时它将转换为readyState 4。任何给定时间下载的总字节数将等于存储在responseText中的字符串中的总字节数。
对于上传问题的全有或无的方法,由于必须传递一个字符串进行上传(并且可以确定该字符串的总字节数),因此准备状态0和1发送的总字节数为0,准备状态2的总字节数将是您传递的字符串中的总字节数。在准备状态3和4中发送和接收的总字节数将是原始字符串中的字节数加上responseText中的总字节数的总和。

3

<!DOCTYPE html>
<html>
<body>
<p id="demo">result</p>
<button type="button" onclick="get_post_ajax();">Change Content</button>
<script type="text/javascript">
 function update_progress(e)
 {
   if (e.lengthComputable)
   {
     var percentage = Math.round((e.loaded/e.total)*100);
     console.log("percent " + percentage + '%' );
   }
   else 
   {
    console.log("Unable to compute progress information since the total size is unknown");
   }
 }
 function transfer_complete(e){console.log("The transfer is complete.");}
 function transfer_failed(e){console.log("An error occurred while transferring the file.");}
 function transfer_canceled(e){console.log("The transfer has been canceled by the user.");}
 function get_post_ajax()
 {
    var xhttp;
    if (window.XMLHttpRequest){xhttp = new XMLHttpRequest();}//code for modern browsers} 
   else{xhttp = new ActiveXObject("Microsoft.XMLHTTP");}// code for IE6, IE5    
    xhttp.onprogress = update_progress;
  xhttp.addEventListener("load", transfer_complete, false);
  xhttp.addEventListener("error", transfer_failed, false);
  xhttp.addEventListener("abort", transfer_canceled, false);    
    xhttp.onreadystatechange = function()
    {
      if (xhttp.readyState == 4 && xhttp.status == 200)
      {
         document.getElementById("demo").innerHTML = xhttp.responseText;
      }
    };
   xhttp.open("GET", "http://it-tu.com/ajax_test.php", true);
   xhttp.send();
 }
</script>
</body>
</html>

Result


1

如果您可以访问您的Apache安装并信任第三方代码,您可以使用apache上传进度模块(如果您使用Apache;还有nginx上传进度模块)。

否则,您需要编写一个脚本,以便您可以在带外请求文件状态(例如检查tmp文件的文件大小)。

我相信Firefox 3正在进行一些工作,以添加浏览器上传进度支持,但这不会进入所有浏览器并被广泛采用一段时间(更多的是遗憾)。


-8

只有使用纯 JavaScript 实现某种轮询机制才能够实现。您需要定期发送 Ajax 请求(例如每 5 秒)以获取服务器接收到的字节数。

更有效的方法是使用 Flash。flex 组件 FileReference 定期分派“progress”事件,其中包含已上传的字节数。如果您必须使用 JavaScript,则可在 ActionScript 和 JavaScript 之间使用桥连接。好消息是,这项工作已经为您完成了 :)

swfupload

此库允许在 flash 进度事件上注册 JavaScript 处理程序。

这个解决方案的巨大优势在于不需要在服务器端添加额外的资源。


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