Seconda parte (leggi la prima parte, leggi la terza parte)

In questa seconda parte dell’articolo inizieremo a scrivere un po’ di codice e a chiarire altre cosette.

Nella prima parte dell’articolo eravamo rimasti alla installazione dell’estensione APC di PHP.

Il sistema che ci interessa attivare in APC non è però attivo per default (probabilmente “costa” qualche ciclo di CPU): occorre configurare APC in modo da renderlo disponibile. Apriamo il file di configurazione di APC (in Ubuntu è /etc/php5/apache2/conf.d/apc.ini, in altre distribuzioni è sufficiente modificare il php.ini) e aggiungiamo quanto segue:

apc.enabled = 1
apc.rfc1867 = 1

Aldilà della mera attivazione di APC nel suo complesso, la chiave che qui ci interessa particolarmente è “rfc1867″. Indovinate un po’ infatti di cosa parla l’rfc1867… Rifate partire Apache e saremo pronti per il passo successivo.

Abbiamo visto che APC è necessario in quanto tramite di esso è possibile interrogare il PHP per sapere a che punto sia arrivato l’upload. La prima cosa fare, per innescare questo meccanismo, è informare APC che una nostra form ha bisogno del servizio di “monitoraggio upload”. Effettueremo questa operazione inserendo, tra i campi della nostra form, anche un campo “civetta” che servirà proprio a svegliare il prezioso meccanismo di APC.

Ecco dunque una ipotetica form di upload di un file che vuole sfruttare questo meccanismo:


<form action="upload.php" method="post" enctype="multipart/form-data">
  <div>
    <input type="hidden" id="apcid" name="APC_UPLOAD_PROGRESS" value="<?php print uniqid() ?>">
    <input type="file" size="40" id="userfile" name="userfile">
    <input type="submit" value="Inizia il caricamento">
  </div>
</form>

La form è una normalissima form impostata per l’upload di un file, con più uno strano campo. Il suo nome deve essere necessariamente “APC_UPLOAD_PROGRESS” e il valore dello stesso dovrà essere quanto più possibile univoco (vedremo perché). Uno modo semplice semplice per generare un valore univoco è quello di usare time() oppure, ancora meglio, uniqid() (come nel caso di esempio); il significato di questo campo è abbastanza intuitivo: quel valore servirà ad APC come “chiave” univoca per identificare il nostro file. Il solo nome del file non sarebbe stato ovviamente sufficientemente sicuro (more on this later).

Adesso, non appena spediremo la form al server, APC prenderà in carico l’onere di tenerci aggiornati sull’avanzamento dell’upload.

Già, ma come? È il momento di spostarci lato server.

Il file upload.php, menzionato come “action” della nostra form, non ci interessa minimanente. Quello farà ancora il suo solito mestiere, preoccupandosi di gestire eventuali errori e alla fine del caricamento spostare il file dove meglio crede.

Quello che ci serve, invece, è un nuovo script – che chiameremo progress.php – il quale ci permetterà di sapere lo stato di avanzamento.

Qualcosa che più o meno faccia così:


<?php

print_r(getUploadStatus());

function getUploadStatus() {
    if (!extension_loaded('apc') || !function_exists('apc_fetch'))
      return NULL;

    if (!ini_get('apc.enabled') || !ini_get('apc.rfc1867'))
      return NULL;

    $apcid = trim($_GET['apcid']);

    if (empty($apcid))
      return NULL;

    $status = apc_fetch('upload_' . $apcid);

    if (!$status) {
      return NULL;
    }

    if ($status['total'] > 0) {
      $status['percent'] = ceil($status['current'] / $status['total'] * 100);
      $bytes_per_sec = $status['current'] / (time() - ceil($status['start_time']));
      $status['rate'] = ceil($bytes_per_sec / 1024);
      $status['ETA'] = ceil((($status['total'] - $status['current']) / $bytes_per_sec) / 60);
    }

    return $status;
}

All’inizio dello script, da bravi ragazzi quali siamo, controlliamo che quello che ci serve sia a nostra disposizione. Superati i controlli prendiamo dalle variabili in GET un ipotetico parametro “apcid” (il nome l’ho scelto io, non è importante). Il valore di questo parametro dovrà essere esattamente quello che è stato generato dal form che abbiamo visto all’inizio, ovvero il valore della campo di form APC_UPLOAD_PROGRESS. Immaginiamo dunque di poter eseguire questo script tramite un URL del tipo:

http://localhost/~claudioc/progress.php?apcid=4ad4d0b7682f4

Non ci chiederemo, per ora, come fare a conoscere quel codice… le vie del JavaScript sono infinite, e l’argomento del prossimo articolo!

Per adesso preoccupiamoci solo di capire la tecnica: la parte importante è la riga successiva dello script, dove viene invocata (finalmente) la funzione apc_fetch();

Come ho accennato nella prima parte di questo articolo, APC può anche essere usato come sistema di storage key/value – un sistema, cioè, che permette di salvare in maniera semi permanente dei dati attraverso richieste HTTP successive; un po’ come fa il più famoso memcache. La funzione per reperire questi dati una volta salvati è – guarda caso – proprio apc_fetch(). Ecco dunque svelato il meccanismo: una volta attivato l’upload progressivo tramite il campo APC_UPLOAD_PROGRESS della form, APC inizierà internamente ad aggiornare un valore della sua cache corrispondente alla chiave che si chiamerà “upload_<ilcodiceunivocochehoscelto>”. Ecco dunque perché il valore di quel campo dovrà essere univoco: è una chiave all’interno di un “database” (la cache di APC).

E che dati saranno salvati in corrispondenza di quella chiave? Ovviamente tutti quelli che ci interessano per monitorare l’upload (sono predefiniti): il valore di “current” (quanti byte trasferiti), il “total” (il numero di byte da trasferire), lo “start_time” (il timestamp in cui è iniziato il trasferimento), più altre cosette che vedremo in un secondo momento.

Nello script oltre ai dati presenti ve ne inserisco altri “just for fun”. Visto che i dati ce li abbiamo, perché non calcolare anche il tempo stimato alla fine del caricamento e la velocità approssimativa dell’upload?

Prima di chiudere questo articolo, vi faccio conoscere un altro amichetto…

Il primo problema che vi troverete ad affrontare nel provare questa (o altre) soluzioni per il trasferimento di file, è che la vostra connessione in localhost è troppo veloce! I file passano dallo 0% al 100% in un attimo. Come fare a simulare una connessione casalinga? O anche qualcosa di ancora più infimo, un modem 56K?

Installatevi trickle.

$ apt-cache search trickle
trickle - user-space bandwidth shaper

I traffic shaper, solitamente, sono delle bestiacce: configurarli è un po’ una pena. Trickle invece è simpaticissimo: eseguite qualsiasi programma tramite di esso, e questo programma avrà la banda tagliata quanto decidete voi.

Un esempio vale più di cento parole: vogliamo simulare un Firefox di un utenza con 80KB/s di banda? Ecco:

trickle -s -d 80 -u 80 firefox

That’s it; usando quell’istanza di firefox per le prove la vostra banda sarà garantita essere non più di 80KB/s e vedrete con soddisfazione i vostri numerelli crescere piano piano.

A risentirci alla prossima puntata :)