File upload con Ajax

AJAX, o per essere più precisi l’oggetto XMLHttpRequest (XHR), sono una gran bella cosa e permettono di rendere molto più dinamiche e gradevoli le pagine web. Questo meccanismo ha però una mancanza che salta fuori nel momento in cui si stanno realizzando delle interfacce che permettono l’upload di file: semplicemente non è possibile.

Sempre più spesso si fa uso di Ajax per realizzare delle interfacce che rendano più gradevole, più pratiche e più funzionali le vecchie pagine web col contenuto statico. La differenza sta nel fatto che nel momento di inviare un form non devo aspettare che la pagina si ricarichi, ma solo che venga inviato il form e che arrivino le informazioni di ritorno. Ciò equivale a minor tempo perso da parte dell’utente, a un minor carico sul server e a un minor consumo di banda.

Dicevamo però dell’impossibilità di uploadare dei file. Anche se si potrebbe costruire una richiesta POST ed inserire nel payload il contenuto del file ci mancherebbe comunque la materia base: il file stesso! Tra i tanti limiti di sicurezza imposti a Javascript c’è l’impossibilità di accedere al filesystem. È una misura necessaria, ma in questo caso limita le possibili applicazioni.

Per fortuna però esiste un workaround che, sebbene non abbia nulla a che fare con AJAX, contente di realizzare comunque una funzionalità dal comportamento “ajaxoso”.

Concettualmente il problema è risolto in maniera veramente semplice: si imposta come target del form un iframe invisibile. Troppo sintetico? Allora andiamo a vedere un esempio concreto.

Come prima cosa ci serve un form che preveda l’invio di un file e che abbia come target un iframe invisibile.

<form id="image-upload-form" action="upload.php" method="post" enctype="multipart/form-data" target="image-upload-iframe">
<input type="file" name="file" />
<input type="submit" value="Carica il file!"/>
</form>
<iframe name="image-upload-iframe" src='#' style='display:none'></iframe>

Passo due: il file PHP che si occuperà di caricare il file.

<?php
move_uploaded_file($_FILES['file']['tmp_name'], $_FILES['file']['name']);
?>
<!DOCTYPE html>
<html>
<head></head>
<body></body>
</html>

L’esempio minimale che ho riportato ha il difetto di non rendere molto ajaxoso il processo. L’upload funziona, ma all’utente non viene fornito nessun tipo di riscontro. Aggiungiamo quindi un po’ di javascript che permette di migliorare le cose.

Ecco il nuovo codice HTML:

<form id="image-upload-form" action="upload.php" method="post" enctype="multipart/form-data" target="image-upload-iframe" onsubmit="preLoad()">
<input type="file" name="file" />
<input type="submit" value="Carica il file!"/>
</form>
<iframe name="image-upload-iframe" src='#' style='display:none'></iframe>

Il nuovo codice PHP:

<?php
$error = 0;
$result = 'true';
if ($_FILES["file"]["error"] != UPLOAD_ERR_OK) {
 $result = 'false';
 $error = $_FILES["file"]["error"];
} else if (move_uploaded_file($_FILES['file']['tmp_name'], $_FILES['file']['name']) === false) {
 $result = 'false';
 $error = -1;
}
?>
<!DOCTYPE html>
<html>
<head></head>
<body onload="window.parent.postLoad(<?=$result?>, '<?=$error?>')"></body>
</html>

A questo punto basterà definire due funzioni javascript come queste nella pagina che contiene il form.

function preLoad() { ... }
function postLoad(result, error) { ... }

Queste funzioni possono essere usate, per esempio, per far comparire e scomparire uno spinner. La postLoad ha anche la possibilità di segnalare un eventuale errore di caricamento, indicandone anche il tipo.

Potete scaricare da qui un esempio completo

Buon divertimento :-)files/files/

7 Commenti per “File upload con Ajax”

Commento di
andrea
Inserito: 25/08/2010 21:45

Ma non manca un echo?
da
a <body onload="window.parent.postLoad()”>

Commento di
andrea
Inserito: 25/08/2010 21:52

Ha rimosso il codice all’interno delle parentesi: <?php echo $result,’, ‘,$error;?>

Commento di
Caribe 1999
Inserito: 26/08/2010 07:02

Hai ragione, ecco a cosa servono i commenti 😉
Ho corretto e ho anche inserito un esempio scaricabile.

Commento di
Simone
Inserito: 03/09/2010 21:23

grazie per lo script…
ma se volessi introdurre una restrizione sulla dimensione ed estensione dei file da caricare?

Commento di
Caribe 1999
Inserito: 04/09/2010 08:30

Per la dimensione non puoi fare un controllo a priori perché non c’è modo di sapere nulla sul file oltre al suo nome. L’unica cosa fattibile è quella di dire a PHP qual’è la dimensione massima desiderata attraverso un apposito parametro nascosto:

<input type="hidden" name="MAX_FILE_SIZE" value="500" />

Il valore indica la dimensione in KB, quindi 500 KB in questo caso.

Invece per il tipo di file possiamo per esempio sfruttare la funzione preLoad(). Volendo consentire solo determinati tipi di file (per esempio delle immagini) possiamo inserire un controllo del genere:

if (document.getElementById('image-upload-form').file.value.search(/.(jpg|jpeg|png|gif)$/) == -1) { alert('Puoi caricare solo immagini'); return false; }

Quel “return false” è fondamentale per evitare il submit del form.

Commento di
Sergio
Inserito: 17/06/2014 19:35

Bello script, semplice e chiaro.
Un bellissimo punto di partenza per implementare applicazioni.

Commento di
Renato
Inserito: 25/06/2015 20:25

Complimenti! un esempio funzionante al primo colpo!
In rete dozzine di esempli incompleti!
Questo invece fa tornare la voglia di studiare!
Complimenti davvero! Grazie!!

Commenta