Upload progress bar com PHP 5.4

A versão 5.4 do PHP traz inúmeras atualizações e novas características, e dentre elas, está a possibilidade de monitorar o progresso do upload de arquivos via formulários, que antes só era possível mediante instalação de componentes PECL ou aplicando patches no código-fonte do PHP antes de compilá-lo.

Se você fizer uma consulta às configurações do PHP, verá que existem as novas session.upload_progress.enabled, session.upload_progress.cleanup, session.upload_progress.prefix, session.upload_progress.name, session.upload_progress.freq e session.upload_progress.min_freq. Estas variáveis definem o comportamento do PHP no momento da detecção e atualização das informações dos uploads de arquivos.

Para implementar uma barra de progresso que mostrará ao usuário as informações do upload depois que o formulário teve seu envio disparado, fiz algum código para exemplificar o processo. O código foi feito a partir de uma instalação "limpa" do Apache e do PHP.

upload.php (este é o arquivo que contém o formulário e as declarações de CSS e JavaScript necessárias, que podem estar em arquivos separados e chamados via SRC e HREF)
<?php

$is_PHP54_installed = ($x = @ini_get('session.upload_progress.name'));

?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>PHP 5.4 Upload Progress Bar Demo<?php echo (!$is_PHP54_installed ? ' (will not working, installed PHP version is ' . phpversion() . ')' : '') ?></title>

<script type="text/javascript" src="https://www.google.com/jsapi"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>

<style type="text/css">
#upload_progress_status {
  display: none;
  width: 50%;
  margin: 20px 0px 0px 0px;
  overflow: hidden;
}

#upload_progress_status .bar,
#upload_progress_status .bar .progress {
  display: block;
  line-height: 20px;
  overflow: hidden;
}

#upload_progress_status .bar {
  border: 1px solid #000;
}

#upload_progress_status .bar .progress {
  width: 0%;
  background-color: #000;
  color: #fff;
  font-size: 12px;
  font-weight: bolder;
  text-align: center;
}

#form {
  display: block;
}

#loading {
  display: none;
}
</style>

<script type="text/javascript">
<!--
  $j = jQuery;

  upload_progress_status_timer = <?php echo (int)(@ini_get('session.upload_progress.min_freq')) * 1000 ?>;

  upload_progress_status_load = function () {
    try { upload_progress_status_load_XMLHTTP.abort(); } catch (e) {}

    upload_progress_status_load_XMLHTTP = $j.ajax ({ url: "upload_progress_status.php",
      dataType: "json",
      error: function () {
        setTimeout(function () { upload_progress_status_load(); }, upload_progress_status_timer);
      },
      success: function (info) {
        var ups = $j("#upload_progress_status"),
          bu = $j("#upload_progress_status .bytes_processed"),
          cl = $j("#upload_progress_status .content_length"),
          et = $j("#upload_progress_status .elapsed_time"),
          pbp = $j("#upload_progress_status .bar .progress");

        if (info != null) {
          ups.show();

          bu.html(info.bytes_processed_human);
          cl.html(info.content_length_human);
          et.html(info.elapsed_time_human);
          pbp.stop().animate({ width: info.uploaded }, upload_progress_status_timer).html(info.uploaded);

          if (!info.done) setTimeout(function () { upload_progress_status_load(); }, upload_progress_status_timer);
          else ups.hide();
        }
      }
    });
  }

  form_loading = function () {
    $j("#form").hide();
    $j("#loading").show();
  }
-->
</script>
</head>
<body>
<div id="form">
  <form action="<?php echo $_SERVER['PHP_SELF'] ?>" method="post" enctype="multipart/form-data"<?php if ($is_PHP54_installed) { ?> onsubmit="form_loading(); upload_progress_status_load()"<?php } ?>><?php
  if ($is_PHP54_installed) { ?><input type="hidden" name="<?php echo $x ?>" value="demo" /><?php }

  for ($k=0; $k<=4; $k ) {
    ?>File <?php echo $k 1 ?>: <input type="file" name="files[<?php echo $k ?>]" size="30" /><br/><?php
  }

  ?><input type="submit" value="Send files" />
  </form>

  <?php if (count($_FILES)) echo '<pre>' . print_r($_FILES, true) . '</pre>' ?>
</div>

<div id="loading">Sending...</div>

<div id="upload_progress_status">
  <div class="bar"><div class="progress">0%</div></div>
  <span class="bytes_processed">0</span> of <span class="content_length">0</span><br/>
  <span class="elapsed_time">00:00</span> elapsed time
</div>
</body>
</html>

upload_progress_status.php (este é o arquivo que retornará ao JavaScript as informações do progresso do upload)
<?php

@session_start();

function get_user_readable_filesize ($fs) {
  $fsl = array('bytes', 'KB', 'MB', 'GB', 'PB', 'YB');
  $y = 0;
  while ($fs >= 1024) {
    $fs /= 1024;
    $y ;
  }
  return number_format($fs, $y ? 2 : 0, '.', '') . ' ' . $fsl[$y];
}

if (($info = @$_SESSION[ini_get('session.upload_progress.prefix') . 'demo'])) {
  $info['content_length_human'] = get_user_readable_filesize($info['content_length']);
  $info['bytes_processed_human'] = get_user_readable_filesize($info['bytes_processed']);

  $info['elapsed_time'] = time() - $info['start_time'];
  $info['elapsed_time_human'] = sprintf('%02d', (int)($info['elapsed_time'] / 60)) . ':' . sprintf('%02d', (int)($info['elapsed_time'] % 60));

  $info['uploaded'] = number_format(($info['bytes_processed'] * 100 / $info['content_length']), 2, '.', '') . '%';

  $x = $info['files'];
  unset($info['files']);
  $info['files'] = $x;
}

echo @json_encode($info);

?>
Acrescentei alguns itens no array de informações, que originalmente não existem nas que o PHP cria, como o cronômetro do upload, as informações de tamanho total e já enviado em um formato mais legível, e a porcentagem do total de dados já enviados.

Você pode customizar os arquivos, adicionando estilos CSS, eventos JavaScript, valores de retorno JSON, enfim, o que sua criatividade e necessidade exigirem. O essencial está na variável de sessão que está declarada no arquivo upload.php e no campo hidden declarado no formulário. São eles que tornam possível o monitoramento do progresso do upload no PHP 5.4.0 ou superior.

Postar um comentário