Javascript 如何从XMLHttpRequest获得进度

Javascript 如何从XMLHttpRequest获得进度,javascript,ajax,progress-bar,Javascript,Ajax,Progress Bar,是否可以获取XMLHttpRequest的进度(上传字节,下载字节) 当用户上传大文件时,显示进度条会很有用。标准API似乎不支持它,但可能在任何浏览器中都有一些非标准的扩展?毕竟,这似乎是一个非常明显的特性,因为客户端知道上传/下载了多少字节 注意:我知道“轮询服务器以获取进度”选项(这就是我现在正在做的)。这方面的主要问题(除了复杂的服务器端代码)是,通常在上传大文件时,用户的连接被完全阻塞,因为大多数ISP提供的上游服务都很差。因此,提出额外的要求并不像我希望的那样反应迅速。我希望有一种方

是否可以获取XMLHttpRequest的进度(上传字节,下载字节)

当用户上传大文件时,显示进度条会很有用。标准API似乎不支持它,但可能在任何浏览器中都有一些非标准的扩展?毕竟,这似乎是一个非常明显的特性,因为客户端知道上传/下载了多少字节


注意:我知道“轮询服务器以获取进度”选项(这就是我现在正在做的)。这方面的主要问题(除了复杂的服务器端代码)是,通常在上传大文件时,用户的连接被完全阻塞,因为大多数ISP提供的上游服务都很差。因此,提出额外的要求并不像我希望的那样反应迅速。我希望有一种方法(可能是非标准的)来获取这些信息,浏览器随时都可以使用它。

最有希望的方法之一似乎是打开第二个通信通道返回服务器,询问服务器传输完成了多少。

如果您可以访问apache安装并信任第三方代码,则可以使用(如果您使用apache,还可以使用)

否则,您必须编写一个脚本,可以在带外点击以请求文件的状态(例如检查tmp文件的文件大小)


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

对于上传的总数,似乎没有办法处理,但有一些类似于您希望下载的内容。readyState为3后,您可以定期查询responseText以获取下载的所有内容,直到所有内容都可用为止(在IE中不起作用),此时它将转换为readyState 4。在任何给定时间下载的总字节数将等于responseText中存储的字符串中的总字节数


对于上传问题的“全有或全无”方法,由于您必须传递一个用于上传的字符串(并且可以确定该字符串的总字节数),因此为readyState 0和1发送的总字节数将为0,为readyState 2发送的总字节数将为您传递的字符串中的总字节数。readyState 3和4中发送和接收的总字节数将是原始字符串中的字节数加上responseText中的总字节数之和。

使用纯javascript实现这一点的唯一方法是实现某种轮询机制。 您需要以固定的间隔(例如每5秒)发送ajax请求,以获取服务器接收的字节数

更有效的方法是使用flash。flex组件定期发送一个包含已上载字节数的“进度”事件。 如果您需要坚持使用javascript,那么actionscript和javascript之间就有了桥梁。 好消息是,这项工作已经为您完成:)

此库允许在flash progress事件上注册javascript处理程序


此解决方案的优点是不需要服务器端的辅助资源。

Firefox支持。

对于上载的字节,它非常简单。只需监视
xhr.upload.onprogress
事件。浏览器知道必须上载的文件大小和上载数据的大小,因此可以提供进度信息

对于下载的字节(当使用
xhr.responseText
获取信息时),这有点困难,因为浏览器不知道服务器请求中将发送多少字节。在这种情况下,浏览器只知道它接收的字节的大小

有一个解决方案可以解决这个问题,在服务器脚本上设置
Content Length
头就足够了,以便获得浏览器将要接收的字节的总大小

有关更多信息,请访问

例如: 我的服务器脚本读取zip文件(需要5秒钟):

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

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

结果

更改内容 功能更新\u进度(e) { if(如长度可计算) { 风险值百分比=数学四舍五入((e.loaded/e.total)*100); log(“百分比”+百分比+“%”); } 其他的 { log(“由于总大小未知,无法计算进度信息”); } } 函数传输_complete(e){console.log(“传输完成”);} 函数传输失败(e){console.log(“传输文件时出错。”);} 函数传输_cancelled(e){console.log(“用户已取消传输”);} 函数get\u post\u ajax() { var-xhttp; if(window.XMLHttpRequest){xhttp=newxmlhttprequest();}//现代浏览器代码} else{xhttp=newActiveXObject(“Microsoft.XMLHTTP”);}//IE6、IE5的代码 xhttp.onprogress=更新进度; xhttp.addEventListener(“加载”,传输完成,错误); xhttp.addEventListener(“错误”,传输失败,错误); xhttp.addEventListener(“中止”,传输被取消,错误); xhttp.onreadystatechange=函数() { 如果(xhttp.readyState==4&&xhttp.status==200) { document.getElementById(“demo”).innerHTML=xhttp.responseText; } }; xhttp.open(“GET”http://it-tu.com/ajax_test.php“,对); xhttp.send(); }
值得注意的是,“内容长度”不是估计的长度,它必须是精确的长度,太短,浏览器会切断下载,太长,浏览器会认为下载失败。@ChrisChilvers这意味着PHP文件可能无法正确计算,对吗?@nicematt在那个例子中,它来自一个文件就可以了,但是如果你是流式传输zip条带
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(); 
}