Upload di file e variabile $_FILES

Upload di file in PHP e variabile $_FILES

Come altri linguaggi di programmazione, anche PHP consente il caricamento di file su server tramite un form HTML. In questa guida vedremo come effettuare l'upload di file in PHP e come recuperare le informazioni relative a tali file per utilizzarle nelle nostre applicazioni.

Come funziona il caricamento

Prima di analizzare un po' di codice, vediamo quali sono i passaggi che avvengono durante il caricamento di un file:

  1. L'utente accede alla pagina contenente un modulo HTML con un input per selezionare file e un pulsante "Carica file".
  2. L'utente fa clic sul pulsante "Scegli file" e seleziona un file dal suo computer.
  3. Il percorso del file selezionato appare a lato del pulsante "Scegli file", quindi l'utente fa clic sul pulsante di caricamento.
  4. Il file selezionato viene inviato al server e memorizzato in una directory temporanea.
  5. Lo script PHP designato per la gestione del file caricato verifica che il file sia stato caricato con successo, quindi lo copia in una directory specifica.

Upload File PHP

Vediamo nello specifico cosa occorre per riprodurre i passaggi elencati sopra.

Il formato multipart/form-data

Il protocollo HTTP consente il caricamento di file sul server tramite l'utilizzo del formato multipart/form-data e dell'input di tipo file.

Il formato multipart/form-data garantisce che i dati (i file) inviati siano codificati nel formato MIME multipart, utilizzato anche nell'invio delle email e necessario per trasferire dati binari come immagini, video, file zippati, etc...

L'esempio seguente mostra la struttura del form da utilizzare per l'upload di file

<form action="upload.php" method="post" enctype="multipart/form-data">
    <input type="file" name="file">
    <input type="submit" name="upload" value="Carica file">
</form>

Come possiamo vedere, il valore dell'attributo method è post, che è il metodo HTTP utilizzato per l'invio di file al server. L'action contiene il valore upload.php, ossia lo script che si occuperà di gestire il file caricato (lo vedremo nel paragrafo successivo).

Il tag input è di tipo file e consente di mostrare nel browser utente una casella di testo con un pulsante "Seleziona file" (o altra etichetta a seconda del browser utilizzato). È possibile caricare qualsiasi tipo di file, ad esempio immagini, video, file compressi, documenti di Word o PDF e tanti altri tipi di file.

Nota: come vedremo in un tutorial dedicato alla creazione di un form di upload sicuro, è consigliabile verificare il tipo di file caricato per evitare che un utente malintenzionato possa trasferire sul server file dannosi (ad esempio eseguibili).

Una volta selezionato un file dal sistema operativo, per completare l'operazione di upload l'utente deve cliccare sul pulsante "Carica file".

Vediamo adesso come recuperare le informazioni del file in PHP.

Variabile $_FILES

PHP è in grado di recuperare un file inviato al server tramite la variabile superglobale  $_FILES. Essendo un array associativo, possiamo stampare il suo contenuto tramite print_r().

Nel file upload.php andremo ad inserire il seguente codice PHP

<?php
print_r($_FILES);

che produce un output simile a questo

Array
(
    [file] => Array
        (
            [name] => immagine.jpg
            [type] => image/jpeg
            [tmp_name] => C:\xampp\tmp\php599B.tmp
            [error] => 0
            [size] => 23459
        )

)

L'array $_FILES contiene un solo file identificato dall'indice file (l'attributo name nel modulo HTML).

Le informazioni relative a tale file sono le seguenti:

  1. name: il nome del file, nell'esempio immagine.jpg.
  2. type: il tipo di file nel formato MIME. Nell'esempio è image/jpeg in quanto abbiamo caricato un file di tipo immagine.
  3. tmp_name: il percorso della cartella temporanea. Ad ogni upload, PHP genera un nome casuale e lo assegna al file caricato in questa cartella. Es. php599B.tmp.
  4. error: l'errore relativo al caricamento, nel nostro caso 0 equivale a un successo.
  5. size: la dimensione in byte del file caricato.

Funzione move_uploaded_file

Possiamo utilizzare le informazioni presenti nell'array $_FILES per memorizzare il file caricato in una directory specifica. È possibile farlo grazie alla funzione move_uploaded_file().

Vediamo un esempio pratico

<?php
$uploadDir = __DIR__.'\uploads';

foreach ($_FILES as $file) {
    if (UPLOAD_ERR_OK === $file['error']) {
        $fileName = basename($file['name']);
        move_uploaded_file($file['tmp_name'], $uploadDir.DIRECTORY_SEPARATOR.$fileName);
    }
}

Spiegazione codice

  1. Alla riga 1 definiamo la directory definitiva nella quale salveremo i file caricati, quindi cicliamo l'array $_FILES con un foreach.
  2. Alla riga 5 verifichiamo che i file siano stati inviati con successo. In questo caso utilizziamo l'operatore di confronto === per fare un confronto stretto. Se la variabile $file['error'] contiene il valore 0 (che equivale alla costante UPLOAD_ERR_OK) significa che l'invio è andato a buon fine.
  3. Se la condizione alla riga 5 viene soddisfatta, il file temporaneo viene spostato all'interno della directory $uploadDir, tramite la funzione move_uploaded_file().

Nota: abbiamo utilizzato la costante predefinita DIRECTORY_SEPARATOR come separatore delle cartelle. A seconda del sistema operativo in uso, la costante sarà uguale a un backslash "\" (Windows) o a uno slash "/" (Linux).

Alla riga 6 abbiamo utilizzato la funzione basename() per ricavare il nome del file. Questa funzione elimina eventuali percorsi relativi o assoluti presenti nel nome del file. Questo evita il cosiddetto directory traversal, una tipologia di attacco in cui un utente malintenzionato sfrutta una falla dell'applicazione per navigare il file system del server.

Nota: la directory dove verrano salvati i file deve avere i permessi di scrittura abilitati. Su sistemi Linux i permessi devono essere settati a 0755.

Upload multiplo

Lo script precedente consente di caricare un file alla volta. Tramite una semplice modifica al codice HTML del modulo, è possibile caricare più file con una sola richiesta.

Abbiamo a disposizione due modi per fare questo:

  • Aggiungere tanti tag input quanti sono i file che vogliamo caricare.
  • Aggiungere al tag l'attributo multiple e le parentesi quadre [].

Nel primo caso otteniamo il seguente codice HTML

<form action="upload.php" method="post" enctype="multipart/form-data">
    <input type="file" name="file1">
    <input type="file" name="file2">
    <input type="file" name="file3">
    <input type="submit" name="upload" value="Carica file">
</form>

mentre nel secondo caso questo codice

<form action="upload.php" method="post" enctype="multipart/form-data">
    <input type="file" name="file[]" multiple> <!-- riga modificata per upload multipli -->
    <input type="submit" name="upload" value="Carica file">
</form>

L'attributo multiple consente la selezione multipla di file (tenere premuto il tasto Ctrl mentre si clicca sui file). Alla fine del caricamento, saranno disponibili nel solito array $_FILES.

Prima di ciclare l'array $_FILES per poter salvare i file nella directory, dobbiamo modificare la sua struttura.

Possiamo editare il codice del file upload.php in questo modo

<?php
$uploadDir = __DIR__.'\uploads';

$files = rearrange($_FILES);

foreach ($files as $file) {
    if (UPLOAD_ERR_OK === $file['error']) {
        $fileName = basename($file['name']);
        move_uploaded_file($file['tmp_name'], $uploadDir.DIRECTORY_SEPARATOR.$fileName);
    }
}

function rearrange($files)
{
    foreach($files as $key1 => $val1) {
        foreach($val1 as $key2 => $val2) {
            for ($i = 0, $count = count($val2); $i < $count; $i++) {
                $newFiles[$i][$key2] = $val2[$i];
            }
        }
    }
    return $newFiles;
}

Abbiamo aggiunto la funzione personalizzata rearrange(), che consente di riorganizzare gli elementi dell'array $_FILES trasformandolo in un array bidimensionale più comodo da ciclare.

Per approfondire segui questo link.

Errori comuni

Durante il caricamento dei file, è possibile imbattersi in qualche errore in fase di esecuzione dello script.

Un tipico errore è il seguente

Warning: move_uploaded_file(...): failed to open stream: No such file or directory in...

Soluzione: questo errore si verifica se il percorso del file nella cartella di destinazione non è valido. Potrebbe essere necessario creare la cartella di destinazione dei file oppure correggere il percorso di destinazione.

Impostazioni php.ini per upload file

Concludiamo questa guida analizzando alcune delle direttive del file php.ini che consentono di cambiare i settaggi relativi all'upload di file

  1. file_uploads: determina se consentire o meno il caricamento di file.
  2. upload_max_filesize: specifica la dimensione massima che può avere un singolo file prima di essere caricato.
  3. post_max_size: imposta la dimensione massima consentita per i dati inviati tramite POST. Deve essere maggiore di upload_max_filesize.
  4. upload_tmp_dir: la directory dove vengono salvati temporaneamente i file caricati, in attesa di spostarli nella cartella definitiva.
  5. max_file_uploads: il numero massimo di file che possono essere caricati tramite una singola richiesta HTTP.

Con la variabile $_FILES chiudiamo la serie di guide sulle superglobali di PHP. Il prossimo argomento sarà incentrato sulla gestione del file system e le operazioni che è possibile fare su file e directory.