Como obter progresso de XMLHttpRequest

É possível obter o progresso de um XMLHttpRequest (bytes enviados, bytes baixados)?

Isso seria útil para mostrar uma barra de progresso quando o usuário estiver carregando um arquivo grande. A API padrão parece não suportá-lo, mas talvez haja alguma extensão não padrão em qualquer um dos navegadores existentes? Parece um recurso bastante óbvio, afinal, já que o cliente sabe quantos bytes foram carregados / baixados.

note: Estou ciente da alternativa “pesquisar o servidor para progresso” (é o que estou fazendo agora). O principal problema com isso (além do complicado código do lado do servidor) é que, normalmente, ao fazer o upload de um arquivo grande, a conexão do usuário é completamente fechada, porque a maioria dos ISPs oferece upstream ruim. Então, fazer solicitações extras não é tão responsivo quanto eu esperava. Eu estava esperando que houvesse uma maneira (talvez não padrão) para obter essa informação, que o navegador tem em todos os momentos.

Para os bytes enviados, é bastante fácil. Apenas monitore o evento xhr.upload.onprogress . O navegador sabe o tamanho dos arquivos que ele deve enviar e o tamanho dos dados enviados, para que ele possa fornecer as informações de progresso.

Para os bytes baixados (ao obter as informações com xhr.responseText ), é um pouco mais difícil, porque o navegador não sabe quantos bytes serão enviados na solicitação do servidor. A única coisa que o navegador sabe nesse caso é o tamanho dos bytes que está recebendo.

Existe uma solução para isso, basta configurar um header Content-Length no script do servidor, para obter o tamanho total dos bytes que o navegador receberá.

Para mais, acesse https://developer.mozilla.org/en/Using_XMLHttpRequest .

Exemplo: Meu script de servidor lê um arquivo zip (leva 5 segundos):

 $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; 

Agora eu posso monitorar o processo de download do script do servidor, porque eu sei que é o comprimento total:

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

Há uma boa discussão sobre o padrão de progresso para o padrão AJAX aqui:

http://ajaxpatterns.org/Progress_Indicator

Uma das abordagens mais promissoras parece estar abrindo um segundo canal de comunicação de volta ao servidor para perguntar quanto da transferência foi concluída.

O Firefox suporta events de progresso de download do XHR .

O Firefox 3.5 suportará o evento de progresso de upload

Para o upload total, parece não haver uma maneira de lidar com isso, mas há algo parecido com o que você deseja fazer o download. Uma vez que o readyState é 3, você pode consultar periodicamente o responseText para obter todo o conteúdo baixado até um String (isso não funciona no IE), até que tudo esteja disponível e em que ponto ele fará a transição para o readyState 4. O total Os bytes baixados a qualquer momento serão iguais ao total de bytes da string armazenada em responseText.

Para uma abordagem de tudo ou nada para a pergunta de upload, já que você precisa passar uma string para upload (e é possível determinar o total de bytes disso) o total de bytes enviados para readyState 0 e 1 será 0, e o total para readyState 2 será o total de bytes na string que você passou. O total de bytes enviados e recebidos em readyState 3 e 4 será a sum dos bytes na string original mais o total de bytes em responseText.

Se você tiver access ao seu apache, instale e confie em código de terceiros, você pode usar o módulo de progresso de upload do apache (se você usa o apache; há também um módulo de progresso de upload do nginx ).

Caso contrário, você teria que escrever um script que você pode bater fora da banda para solicitar o status do arquivo (verificando o tamanho do arquivo do arquivo tmp, por exemplo).

Há algum trabalho acontecendo no firefox 3 Acredito que adicionar suporte de progresso de upload para o navegador, mas isso não vai entrar em todos os navegadores e ser amplamente adotado por um tempo (mais é a pena).

 < !DOCTYPE html>   

result

A única maneira de fazer isso com o javascript puro é implementar algum tipo de mecanismo de pesquisa. Você precisará enviar solicitações ajax em intervalos fixos (a cada 5 segundos, por exemplo) para obter o número de bytes recebidos pelo servidor.

Uma maneira mais eficiente seria usar o flash. O componente flex FileReference despacha periodicamente um evento ‘progress’ contendo o número de bytes já enviados. Se você precisa ficar com o javascript, as pontes estão disponíveis entre actionscript e javascript. A boa notícia é que este trabalho já foi feito para você 🙂

swfupload

Esta biblioteca permite registrar um manipulador de javascript no evento de progresso do flash.

Essa solução tem a vantagem de não exigir resources adicionais no lado do servidor.