Terza parte (leggi la prima parte, leggi la seconda parte)

Dopo aver visto come si debba configurare il sistema per far funzionare l’upload progressivo e aver buttato giù una form HTML e un po’ di PHP, vediamo adesso di chiudere il cerchio parlando dell’ultima cosa di cui abbiamo bisogno: un po’ di JavaScript.

Il flusso delle operazioni sarà il seguente:

  • l’utente seleziona il file da spedire ed inizia il caricamento verso il server (preme il tasto Submit, per intenderci)
  • lato server, il PHP inizia a ricevere il file e APC traccia in tempo reale lo stato di avanzamento (come visto nella seconda parte)
  • sul browser, non appena l’utente ha premuto il tasto di submit della form, uno script JavaScript interrogherà ogni X secondi uno script PHP che gli fornirà le informazioni sullo stato di avanzamento (it’s Ajax, baby)
  • lo script JavaScript utilizzerà queste informazioni per aggiornare l’interfaccia utente (visualizzazione della percentuale di caricamento effettuato)
  • al termine (o all’interruzione) del caricamento il JavaScript eseguirà delle istruzioni

Premetto che negli esempi che seguono userò la sintassi jQuery. Spero comunque siano chiari anche a chi non è un esperto di questa libreria.

Vediamo come potrebbe apparire lo script più semplice possibile (seguite i commenti):

$('#frm-upload').submit(function() {

  var apcid = $(this).find('input[name=APC_UPLOAD_PROGRESS]').val();
  var $status = $('#uploadStatus');

  setTimeout(pollStatus, 2000);

  var polls = 0;

  function pollStatus() {
    $.getJSON("progress.php?apcid=" + apcid, function(data) {

      /* Se la prima volta non otteniamo risultati, qualcosa è andato storto */
      if (!data && polls == 0) {
        $status.html("Errore nel caricamento, il file potrebbe essere troppo grande. Premi 'stop' nel browser e riprova.");
        try {
          stop();
        } catch(e) {
          document.execCommand('Stop');
        }
      }

      /* Verifichiamo se l'upload è terminato oppure cancellato */
      if (data && (data.cancel_upload || data.done)) {
        if (data.cancel_upload) {
          /* Upload cancellato */
        }
        return;
      }

      if (data) {
        /* Aggiorniamo la percentuale di caricamento */
        $status.html(data.filename + ": " + data.percent + "% @ ~" + data.rate + "KB/s (" + data.ETA + " min.)");
      }

      /* Riattiviamo il polling tra 2 secondi */
      setTimeout(pollStatus, 2000);

      polls++;
    });
  }
}

Quello che viene fatto non è altro che temporizzare (ogni due secondi) una richiesta al server, chiedendo lo stato di avanzamento del nostro file. Vediamo un po’ di cose interessanti:

  • viene agganciato l’evento submit della form; è da quel momento che ha inizio il nostro monitoraggio
  • per la visualizzazione dei dati, viene utilizzato un elemento all’interno della pagina con id “status”
  • la prima richiesta al server potrebbe fallire (da qui la necessità di contare i “poll”). Ho notato che questo succede quando per esempio il file è troppo grande rispetto alle dimensioni massime concesse per l’upload
  • quando qualcosa è andato storto (file troppo grande), allora conviene premere virtualmente il tasto “stop” del browser per chiudere la connessione al server. Ci sono un paio di modi per farlo, uno IE e uno “tutti gli altri” (da qui il try/catch)
  • In questo caso trovo più pratica la setTimeout rispetto alla setInterval. In questo modo sono sicuro che in caso di problemi non mi rimanga un timer attivo a massacrarmi il server.
  • “cancel_upload” e “done” sono chiavi della struttura dati che vengono valorizzate automaticamente da APC al termine dell’upload (infatti non erano state descritte nella seconda parte di questo articolo)

OK, fin qui tutto bene (citando un gran bel film).

I problemi arrivano quando l’upload termina. Cosa succede infatti – solitamente – quando si è finito un upload? Si effettua un redirect HTTP da qualche altra parte. In questo caso però – e non ho capito neanche troppo bene perché – il redirect sembra funzionare bene soltanto con Firefox. Gli altri browser si “imbambolano” e, finito l’upload, niente accade. Questo non è un grande problema perché alla fine tutte le altre librerie o gli altri sistemi che ho indagato fanno tutti la stessa cosa, sia per girare intorno a questo problema, sia per ottenere un altro effetto molto simpatico.

Quello che solitamente si fa in questo caso è fare in modo che il target della form sia un IFRAME.

Lo pseudocodice è dunque il seguente:

  • si crea un iframe vuoto (src=”about: blank”) e gli si dà un name
  • si imposta l’attibuto TARGET del tag FORM al name del nostro IFRAME
  • si aggancia l’evento LOAD dell’IFRAME. Sarà questo il modo con cui potremo comunicare delle informazioni dallo script di upload al nostro script JavaScript. L’evento load verrà infatti generato non appena l’upload è terminato.
  • all’interno del event handler del load dell’iframe potremo accedere al contenuto (ovvero ad eventuali dati fornitici dallo script di upload) con $iframe.contents().text()
  • sempre nell’event handler, modificando la location.href potremmo portare il browser sulla pagina di nostra scelta

Intuitivamente si capisce come un altro vantaggio dell’utilizzo dell’iframe è ottenere l’effetto degli upload di file SENZA fare il submit della form; è quello che succede per esempio quando si allegano dei file ad una mail scritta con GMail.

E per adesso è tutto :)