Progresso de Upload com PHP 5.2+

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());?>" />

4) 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

Comentários

imagem de Alux

Cara! Muito bom!!! Eu estava fazendo uma lingüiça com php e várias bibliotecas Java para fazer essa barra maldita hehehehe. Parabéns!
De qualquer forma ainda vou ter que pesquisar mais sobre isso para fazer o formulário prático. Vou assinar o Feed e vou ficar na expectativa de um exemplo desta função. Valeu !!!!

imagem de Marcelo Costa

Olá parabens pelo artigo, realmente este tema é muito escasso em português.
Estou tentanto implementar esta barra de progressão num site que estou desenvolvendo mais estou esbarrando em algumas dúvidas:
No item 2 você fala para mudar o caminho para:
uploadprogress.file.filename_template = /caminho/para/algum_arquivo_%s.txt
Onde devo mudar este caminho no php.ini?
Você fala mais abaixo de chamar a função: uploadprogress_get_info($id), não encontrei esta função no meu código, onde ela deve ficar?

Grato,

imagem de lourenzo

Quote:
uploadprogress.file.filename_template = /caminho/para/algum_arquivo_%s.txt
Onde devo mudar este caminho no php.ini?

Sim, isso você muda no php ini, ou usa a função ini_set em seu aplicativo.

Quote:
Você fala mais abaixo de chamar a função: uploadprogress_get_info($id), não encontrei esta função no meu código, onde ela deve ficar?

Esta função deve estar em uma resposta ajax que será tratada pela interface.

Lourenzo Ferreira

imagem de por

Onde se deve executar "pecl install uploadprogress"?

imagem de lourenzo

Olá!

O comando deve ser executado no terminal bash, no caso de ser Windows eu imagino que no emulador de DOS, vulgo cmd. Nesse caso pode ser necessário configurar o PATH do Windows para que este execute o comando...

Lourenzo Ferreira

imagem de João Claudio Moro

Muito obrigado! vou usar este artigo para instalar no meu servidor local (de testes) a extensão uploadprogress.

imagem de Leonardo

Olá Lourenzo,

Estou tentando instalar o uploadprogress no meu servidor dedicado mas está dando o seguinte erro:


root@whl0001 [~]# pecl install uploadprogress
downloading uploadprogress-1.0.1.tgz ...
Starting to download uploadprogress-1.0.1.tgz (8,536 bytes)
.....done: 8,536 bytes
4 source files, building
running: phpize
Configuring for:
PHP Api Version: 20041225
Zend Module Api No: 20060613
Zend Extension Api No: 220060519
building in /var/tmp/pear-build-root/uploadprogress-1.0.1
running: /root/tmp/pear/uploadprogress/configure
checking for egrep... grep -E
checking for a sed that does not truncate output... /bin/sed
checking for cc... cc
checking for C compiler default output file name... a.out
checking whether the C compiler works... configure: error: cannot run C compiled programs.
If you meant to cross compile, use `--host'.
See `config.log' for more details.
ERROR: `/root/tmp/pear/uploadprogress/configure' failed
root@whl0001 [~]#

Sabe o que pode ser?

No meu servidor dedicado possuo o CentOS 5.3, PHP 5.2.10 e WHM/cPanel 11.24.2.

Abraços!

imagem de lourenzo

Olá!

Parece que está faltando o GCC, o compilador de linguagem C da GNU.

O comando abaixo resolve esta dependência em específico:

yum install gcc

Mas é provável que surjam outras dependências, talvez seja mais interessante procurar uma forma de instalar a extensão utilizando o yum, que verificará todas as dependências automaticamente.

Att,

Lourenzo Ferreira

imagem de Leonardo

Olá Lourenzo,

Agradeço pela atenção.

Consegui instalar normalmente seguindo as recomendações deste blog:

http://freestylesystems.co.uk/blog/installng-pecl-uploadprogress-extensi...

Parabéns pelo artigo, vi no blog da KingHost. :)

Abraços,
Leonardo Cesar Teixeira

imagem de lourenzo

Legal, tinha mesmo esquecido dessa possibilidade.

Inclusive aqui no meu mac eu tive que fazer isso para funcionar o GD...

Lourenzo Ferreira

imagem de pedro

amigo, existe algum exemplo para leigo igual a eu ... ?? Procurei no site http://br.php.net/features.file-upload enão entendi e nem consegui achar onde nem como posso utilizar a barra de progresso do php. Ficaria muito grato se pudesse me ajudar.

imagem de lourenzo

Olá!

Eu cito um exemplo bem simples no post, mostrando como fazer.

Neste artigo: http://ajaxian.com/archives/asynchronous-file-upload-with-ajax-progress-...

Tem uma explicação mais elaborada e um exemplo para download.

O artigo está em inglês.

Lourenzo Ferreira

imagem de pedro renato

Olá Lourenzo, fiquei muito feliz pela resposta.

Fiz alguns testes, perdi algumas horas e nada.
Achei esse arquivo http://t.wits.sg/2008/06/25/howto-php-and-jquery-upload-progress-bar/ fiz como está ai e nada ...
Rapaz, desculpa a pergunta, mas qual a possibilidade de me oferecer um suporte, para fazer essa barra de upload funcionar lá no servidor da King Host ?

Reforço meus agradecimentos.
Att/ Renato

imagem de João Claudio Moro

eu consegui fazer funcionar na kinghost o upload_progress...

bom.. a princípio vc vai ter que ir no painel de controle e desativar o MODSECURITY..

exemplo de formulário que vai enviar o POST:

<?php
// identificador do upload..
$id_up = md5(time());

// se for na internet (kinghost)
if(function_exists("apc_fetch")){
echo '';
}else{
// localmente (ubuntu)
echo '';
}
?>
selecione o arquivo

agora você só precisa ficar passar o $id_up via GET para um arquivo de código que tenha o seguinte:

<?php
// se for na internet (kinghost)
if(function_exists("apc_fetch")){
$upload = apc_fetch('upload_'.$_GET[id]);
if($upload){
if ($upload[done]){
$percent = 100;
}else if ($upload[total] == 0){
$percent = 0;
}else{
$percent = $upload[current] / $upload[total] * 100;
}
$atual = $upload[current];
$total = $upload[total];
}else{
#echo 'Sem informações ainda..';
}
}else{
// localmente (ubuntu)
$upload = uploadprogress_get_info($_GET[id]);
if($upload){
$percent = $upload[bytes_uploaded] / $upload[bytes_total] * 100;
$atual = $upload[bytes_uploaded];
$total = $upload[bytes_total];
}else{
#echo 'Sem informações ainda..';
}
}
// da o feedback
if($percent > 0){
// tranforma para de bytes para Kbytes
$atual = (int)($atual/1024);
$total = (int)($total/1024);
// se for mais que 1mega, mostra em MB, senão mantém em KB
if($atual > 1024){
$atual = (int)($atual/1024).'Mb';
}else{
$atual .= 'Kb';
}
if($total > 1024){
$total = (int)($total/1024).'Mb';
}else{
$total .= 'Kb';
}
echo ''.(int)$percent.'% ('.$atual.' de '.$total.')';
}
#print_r($upload);
?>

você vai ter que usar no mínimo um pouco de javascript.. pra fazer funcionar tudo automaticamente.. (a chama do arquivo que vai ver o andamento de upload)

imagem de Rafael Rudnik de Oliveira

E aí João, parabéns, você conseguiu...

..Então, eu também hospedo na Kinhost, e não consegui fazer funcionar o upload_progress.. , desabilitei o mod_security etc.. diz que tanto a função apc_fecth() quanto a uploadprogress_get_info() não existem, pode me dar alguma sugestão?

msn: raruol3@hotmail.com

Abraço!

imagem de lourenzo

Olá! Tanto a função uploadprogress_get_info() quando a apc_fetch() do Advanced PHP Cache podem rastrear o andamento dos uploads atualmente.

No caso do KingHost é bem provável que não tenham APC (ele deixa tudo muito rápido, mas custa memória RAM), mas você pode pedir que eles ensinem como instalar/utilizar o uploadprogress (extensão do PHP) em seu site.

Att,

Lourenzo Ferreira

imagem de Rafael Rudnik de Oliveira

Eu quero usar no seguinte site:
www.gigantesdanatureza.com.br
acessem em breve, abraços

imagem de João Claudio Moro

Opa.. bah.. agora que vi.. uma parte do código (HTML) que eu postei na mensagem anterior o blog comeu... (ali onde diz selecione o arquivo)...

bah.. se ele diz que não existe a função apc_fetch, eu não sei o que te dizer. Você deve entrar em contato com o suporte e informar sua situação, pedir para verificarem se está instalado o recurso no servidor.

a função uploadprogress_get_info, pelo que me lembro existe (no meu servidor), mas não funciona como deveria, e isto acontece por que o servidor é red hat.. e não há nenhuma informação sobre isso na internet... aparentemente..

bom.. abre um ticket de assistência... qualquer coisa me manda e-mail: psicqs@yahoo.com.br

imagem de lourenzo

Olá!
Abaixo do formulário de comentário há um informativo que lista as tags HTML permitidas em comentários.

Esta restrição ajuda a proteger os leitores de código potencialmente danoso.

apc_fetch é uma função do APC, Advanced PHP Cache, que agora é nativo do PHP. Ela pode ser usada para acompanhar o progresso de uploads também.

Abs

Lourenzo Ferreira

imagem de Rafael Rudnik de Oliveira

Opa! Valew João e Lourenzo...

Obrigado pelas respostas...
Eu vou entrar em contato com o suporte da KingHost, quem sabe eles solucionem meu problema.

Abraços

http://raruol.wordpress.com

imagem de marceli

Parabéns pelo trabalho realizado no blog.

Excelente quarta,

Marceli

Comentar

Usando nome e URL
  • Endereços de páginas de internet e emails viram links automaticamente.
  • Tags HTML permitidas: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Quebras de linhas e parágrafos são feitos automaticamente.
  • [left]Conteúdo[/left] [right]Conteúdo[/right]
  • {{ url [|text] [|title] [|rel] [|target] }}
  • [edit] ..text.. [/edit]
  • Você pode citar outros comentários usando as tags [quote] .

Mais informações sobre as opções de formatação