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.
Indice dei contenuti
Prima di analizzare un po' di codice, vediamo quali sono i passaggi che avvengono durante il caricamento di un file:
Vediamo nello specifico cosa occorre per riprodurre i passaggi elencati sopra.
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.
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:
name
: il nome del file, nell'esempio immagine.jpg
.type
: il tipo di file nel formato MIME. Nell'esempio è image/jpeg
in quanto abbiamo caricato un file di tipo immagine.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
.error
: l'errore relativo al caricamento, nel nostro caso 0
equivale a un successo.size
: la dimensione in byte del file caricato.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
$_FILES
con un foreach.===
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.$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.
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:
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.
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.
Concludiamo questa guida analizzando alcune delle direttive del file php.ini che consentono di cambiare i settaggi relativi all'upload di file
file_uploads
: determina se consentire o meno il caricamento di file.upload_max_filesize
: specifica la dimensione massima che può avere un singolo file prima di essere caricato.post_max_size
: imposta la dimensione massima consentita per i dati inviati tramite POST. Deve essere maggiore di upload_max_filesize
.upload_tmp_dir
: la directory dove vengono salvati temporaneamente i file caricati, in attesa di spostarli nella cartella definitiva.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.