Upload con Php
Premessa
Come è noto a chiunque abbia un minimo di familiarità con il PHP, è estremamente semplice gestire gli Uploads da un computer-client verso un computer-server. Lo scopo di questo articolo è proprio dimostrarlo e realizzare, alla fine, un semplice script che ci consenta di gestire agevolmente il "caricamento" dei file in rete.
Piccola, ma doverosa, premessa: cosa significa Upload?
Letteralmente questa parola inglese significa "caricare" e si contrappone, logicamente, a Download (termine probabilmente più familiare del primo), ossia "scaricare".
In breve indica l'operazione di trasferimento di uno o più files da un client verso un server, operazione che di norma viene svolta avvalendosi del protocollo FTP (File Transfer Protocol) e di appositi programmi (detti appunto client FTP). Ma il trasferimento può anche essere attuato, con PHP, tramite un form con metodo POST; ed è proprio questo il caso che stiamo prendendo in considerazione.
PHP, si diceva, può ricevere files "uploadati" da qualunque browser che sia compatibile con le direttive fissate nella RFC (Request For Comments) 1867 che, nel lontano Novembre 1995 (preistoria per gli standard di Internet...), introduceva un nuovo tipo di opzione per i campi di un modulo di tipo input: l'opzione FILE. Inoltre veniva definito un nuovo MIME (Multipurpose Internet Mail Extensions) media type, denominato multipart/form-data (per maggiori informazioni in merito, si può leggere l'intera RFC a questo indirizzo http://www.ietf.org/rfc/rfc1867.txt ).
Discutine sul ForumCostruzione del modulo html
Fatta questa premessa introduttiva, possiamo adesso cominciare a costruire il nostro script per gli Uploads. Il primo passo non può che essere la realizzazione del modulo HTML che ci servirà per inviare i nostri files al server; ridotto al suo schema essenziale, il form può essere così concepito:

<form action="upload.php" method="post" enctype="multipart/form-data">
<input type="file" name="upfile">
<input type="hidden" name="MAX_FILE_SIZE" value="10000">
<input type="submit" value="Invia il file">
</form> 

Salviamo queste righe in un file chiamato form.html (non è necessaria l'estensione .php, dato che contiene solo istruzioni html).
Un prima considerazione si impone: oltre al campo di tipo FILE, abbiamo aggiunto un campo nascosto (hidden) chiamato MAX_FILE_SIZE con value 10000. Esso indica la 'grandezza' massima (espressa in bytes) del file da caricare e a questo proposito va detto che, come avverte chiaramente anche il manuale ufficiale di PHP, non si deve fare troppo affidamento su questo parametro per "garantirsi" contro gli upload di files di dimensioni maggiori di quella desiderata, perchè il limite è facilmente aggirabile.
Molto più sicuro, a questo fine, è il parametro upload_max_filesize nel file php.ini (il file di configurazione di PHP) che di default è settato a 2 MegaBytes.
Seconda considerazione: adesso che abbiamo il nostro modulo per l'upload, una cosa che balza subito all'occhio è il tasto "sfoglia" (browse) alla destra del campo di tipo FILE; questo bottone serve proprio per selezionare il file dall'hard disk del client che verrà visualizzato (il file, non l'Hard Disk...), all'interno del campo, con il suo percorso assoluto.Le variabili da usare per l'upload
Prima di addentrarci nella realizzazione della pagina PHP che "raccoglierà" i dati provenienti dal modulo, è necessario spiegare cosa accade quando lo script riceve un Upload.
In questa situazione vengono automaticamente definite alcune variabili particolari, caratterizzate anzitutto dal fatto che il loro nome consta di due parti: la prima coincide con il nome del campo di tipo FILE (nel nostro caso quindi upfile); la seconda invece indica un "attributo" del file.
Da notare altresì che queste variabili saranno disponibili all'interno dell'array globale $HTTP_POST_FILES ($_FILES a partire da PHP 4.1.0), sempre che il parametro track_vars, nel file php.ini, sia settato ad ON (il problema, peraltro, si pone per le versioni di PHP precedenti alla 4.03, a partire dalla quale track_vars è automaticamente abilitato).
Queste variabili sono:

$nomefile (il nome temporaneo del file che viene attribuito al file sul server)
$nomefile_name (il nome originario o il percorso del file sul client)
$nomefile_size (la grandezza, espressa in bytes, del file)
$nomefile_type (il MIME type del file, ad esempio application/x-zip-compressed)

Come detto $nomefile si riferisce al nome del campo FILE del form, quindi $upfile nel nostro esempio.

A questo punto però, è doveroso accennare alla grande modifica introdotta dallo staff di PHP a partire dalla versione 4.2.0 (al momento in cui scrivo siamo arrivati alla 4.2.3), ossia il settaggio ad Off di default del parametro register_globals nel file php.ini.
Senza approfondire troppo l'argomento, in quanto esulerebbe dagli scopi di questo scritto, possiamo limitarci a dire che, a seguito di questa novità per riferirsi alle variabili globali, occorre fare riferimento all'array cui esse appartengono (a meno che non si modifichi php.ini, ovviamente). Nel nostro caso, quindi, all'array $HTTP_POST_FILES sostituiamo l'array $_FILES (in seguito vedremo come rendere comunque compatibili anche le versioni di PHP precedenti alla 4.1.0).
Quindi le variabili si "traducono" in:

$_FILES["nomefile"]["tmp_name"] (il nome temporaneo del file che viene attribuito al file sul server)
$_FILES["nomefile"]["name"] (il nome originario o il percorso del file sul client)
$_FILES["nomefile"]["size"] (la grandezza, espressa in bytes, del file)
$_FILES["nomefile"]["type"] (il MIME type del file, ad esempio application/x-zip-compressed).Costruiamo la pagina Php per l'upload

Cominciamo quindi a costruire la pagina .php che si occuperà di verificare l'upload e di gestire il file come vogliamo. Per comodità, preferisco scrivere prima tutto il codice e poi spiegarlo passo passo, con l'avvertenza che anche qui, come nel form, lo script sarà semplice, senza troppe opzioni.

<?
// QUESTE RIGHE RENDONO LO SCRIPT COMPATIBILE CON LE VERSIONI
// DI PHP PRECEDENTI ALLA 4.1.0
if(!isset($_FILES)) $_FILES = $HTTP_POST_FILES;
if(!isset($_SERVER)) $_SERVER = $HTTP_SERVER_VARS;
/********************* VARIABILI DA SETTARE ********************/
// Directory dove salvare i files Uploadati ( chmod 777, percorso assoluto)
$upload_dir = $_SERVER["DOCUMENT_ROOT"] . "/upload";
// Eventuale nuovo nome da dare al file uploadato
$new_name = "";
// Se $new_name è vuota, il nome sarà lo stesso del file uploadato
$file_name = ($new_name) ? $new_name : $_FILES["upfile"]["name"];
if(trim($_FILES["upfile"]["name"]) == "") {
die("Non hai indicato il file da uploadare !");
}
if(@is_uploaded_file($_FILES["upfile"]["tmp_name"])) {
@move_uploaded_file($_FILES["upfile"]["tmp_name"], "$upload_dir/$file_name")
or die("Impossibile spostare il file, controlla l'esistenza o i permessi della directory dove fare l'upload.");
} else {
die("Problemi nell'upload del file " . $_FILES["upfile"]["name"]);
}
echo "L'upload del file " . $_FILES["upfile"]["name"] . " è avvenuto correttamente";
?> 

Le prime righe, come chiarisce il commento, servono a rendere disponibile la sintassi dei nuovi array introdotti con PHP 4.1.0 anche alle versioni precedenti.
Le due variabili da settare sono rispettivamente:

la directory dove spostare i files uploadati ($upload_dir, da indicare con il percorso assoluto sul server e i cui permessi vanno settati a 777)

l'eventuale nuovo nome da dare al file che viene caricato sul server ($new_name; se non si vuole rinominare il file, questo avrà lo stesso nome che aveva sul computer client.)
A questo punto effettuiamo un controllo per verificare se è stato selezionato un file per l'Upload, e lo facciamo con riferimento alla proprietà "name" del file.
Andata a buon fine questa verifica, possiamo passare al cuore dello script, la parte cioè che "sposta" il file caricato in rete dalla directory temporanea del server (che si può settare in php.ini, nella variabile upload_tmp_dir) alla directory da noi indicata, eventualmente rinominandolo.
Le funzioni usate a questo scopo, sono is_uploaded_file() (PHP 3>= 3.0.17, PHP 4 >= 4.0.3) e move_uploaded_file() (PHP 4 >= 4.0.3, eventualmente si può usare copy() in sostituzione). La prima funzione prende come parametro la variabile che contiene il nome temporaneo del file uploadato sul server ($_FILES["upfile"]["tmp_name"]) e verifica, appunto, se sia stato caricato; la seconda funzione prende due parametri: il primo è il nome temporaneo ed il secondo è il percorso finale (comprensivo del nome, nuovo o meno, del file) dove spostarlo.
A questo proposito vi segnalo che è possibile, nell'ipotesi in cui si disponga di una versione di PHP in cui non sia presente la funzione is_uploaded_file(), "costruirne" una che svolga lo stesso compito, come evidenziato dal manuale ufficiale a questo link: http://www.php.net/manual/it/features.file-upload.php.
Se anche queste operazioni si svolgono senza problemi (nel caso di difficoltà, lo script restituirà solo messaggi d'errore personalizzati), verrà stampata a video la frase che ci conferma che l'upload è avvenuto correttamente.

Conclusioni
Tutto molto semplice come si vede. Ovviamente è possibile implementare ulteriori controlli all'interno dello script; ad esempio, sfruttando la proprietà "type", si può agevolmente limitare l'upload solo a determinati tipi di files aggiungendo poche righe subito dopo il controllo sull'esistenza del file uploadato:

$allowed_types = array("image/gif","image/x-png","image/pjpeg","image/jpeg");
if(!in_array($_FILES["upfile"]["type"],$allowed_types)) {

die("Il file non è di un tipo consentito, sono ammessi solo i seguenti: " . implode(",", $allowed_types) . ".");

}

?> 


Sulla base di questo esempio, sarebbe consentito solo l'upload di immagini .gif, .png e .jpg (o .jpeg).