O desafio é criar uma barra de monitoramento de progresso para uploads de arquivo, quando se está utilizando AJAX.

Pode até se achar que isso é frescura, mas no caso de envio de arquivos grandes, isso pode ser bastante útil.

Há muita gente na web dizendo que é impossí­vel fazer isso apenas com PHP. Até certo ponto isso é verdade, pois até pouco tempo, o PHP só obtinha acesso a REQUESTs completos, e o progresso de upload, obviamente, só pode ser medido durante o REQUEST.

A solução encontrada foi usar Perl em paralelo, num bem-bolado que pode trazer problemas. O fornecido script CGI feito em Perl recebe o POST, e consegue acessar os dados do REQUEST enquanto ele está em andamento.

Exemplo: http://br.php.net/features.file-upload (Em inglês)

Isso seria a única saída para o problema no caso de se usar uma versão do PHP inferior à 5.2. Isso porque, da versão 5.2 em diante, o PHP suporta um gancho que pode ser usado para lidar com o progresso do upload. Você pode usar uma extensão PECL já existente (uploadprogress) para mostrar um medidor do progresso de um upload.

Tendo em vista que a documentação para essa extensão é escassa, aqui vai uma simples receita de bolo.

  1. Execute “pecl install uploadprogress”, e adicione “extension=uploadprogress.so” ao seu arquivo php.ini .

  2. Diga à extensão onde armazenar temporariamente as informações sobre cada upload. Por padrão, isso será guardado em “/tmp/upt_%s.txt” (onde %s deve ser substituído pela variável UPLOAD_IDENTIFIER, como será descrito abaixo). Você pode mudar isso na através da seguinte linha no arquivo de configuração: uploadprogress.file.filename_template = /caminho/para/algum_arquivo_%s.txt Você deve adicionar apenas um ‘%s’, ou a coisa toda irá falhar

  3. Adicione um campo oculto bem no início do seu formulário de upload (isso é importante) , com o nome de UPLOAD_IDENTIFIER. O valor para esse campo deve combinar com a expressão regular “^[A-Za-z0-9_.-=]{1,64}$” , ou seja, possuir de 1 a 64 caracteres alfanuméricos, sendo que as letras podem ser maiúsculas ou minúsculas. Além disso, o valor deve ser único para cada upload. Exemplo:

<input type="hidden"
name="UPLOAD_IDENTIFIER"
value="upld<?php md5(time());?>" />
  1. Agora vem a parte da diversão… Mostrar a barra de progresso. Vou descrever uma maneira bem simples de fazer isso, deixando bem claro que é possível fazer isso com muito mais requinte.

Quando o formulário for submetido, abra um popup aproveitando a ação do usuário (o click pode ajudar seu popup a não ser bloqueado) para mostrar o progresso. Essa janela deve ser atualizada repetidamente de poucos em poucos segundos, chamando um script que irá tratar os dados e exibir as informações, e gerar um medidor.

Esse script chama a função uploadprogress_get_info($id), onde $id é o valor do campo UPLOAD_IDENTIFIER do seu formulário. A função retornará falso caso não haja um upload relacionado, ou um array de informações sobre o upload. O array contém:

time_start
Data/Hora do início do upload (no mesmo formato da função time()).
time_last
Data/Hora da última atualização do progress de upload.
speed_average
Velocidade média. (bytes / segundo)
speed_last
Última velocidade medida. (bytes / segundo)
bytes_uploaded
Número de bytes recebidos pelo servidor até o momento.
bytes_total
O valor do cabeçalho HTTP "Content-Length" enviado pelo navegador.
files_uploaded
Número de arquivos já recebidos pelo servidor.
est_sec
Número estimado de segundos restantes para o término do processo de upload.

O valor speed_average é medido com base no número de bytes obtidos pelo servidor desde o início do upload, enquanto speed_last é baseado no número de bytes enviados desde a última atualização no progresso do upload. A informação sobre o progresso é atualizada cada vez que o PHP obtêm mais dados do cliente, então speed_last pode não ser muito preciso.

Nota 1) O valor bytes_total NÃO é reflexo do tamanho real do arquivo, mas sim do tamanho do REQUEST POST, que pode ser maior que o tamanho real do arquivo transferido.

Nota 2) Esse módulo realmente detecta apenas quanto do formulário com método POST foi recebido pelo servidor, e mantém uma contagem de quantas variáveis POST do tipo ‘file’ vão sendo encontradas. Então quando forem enviados vários arquivos em um mesmo formulário, não é possível fazer uma medição de progresso para cada arquivo, mas é possível obter a contagem de quantos arquivos já foram completamente transferidos.

Fonte: Documentação online do PHP, em um dos comentários: http://br.php.net/manual/pt_BR/features.file-upload.php#71564