Skip to content

Tutto per WordPress, lo sviluppo web — e non solo

📤 Caricamento file con trascinamento usando Dropzone.js e PHP

📤 Caricamento file con trascinamento usando Dropzone.js e PHP

Gli utenti si aspettano di caricare file su un sito web semplicemente trascinandoli nella finestra del browser. Il pulsante «Scegli file» e l'esploratore di file sono già percepiti come passaggi superflui. Ma scrivere da zero la funzionalità drag-and-drop in JavaScript significa diverse centinaia di righe di codice, barre di avanzamento, anteprime e gestione degli errori. Per fortuna esiste una libreria già pronta che copre tutto questo out of the box.

Dropzone.js è una libreria JavaScript open-source per il caricamento di file tramite drag-and-drop. Mostra anteprime delle immagini, una barra di avanzamento e non richiede jQuery. Funziona con qualsiasi linguaggio lato server: PHP, Node.js, Python. In questa guida trovi il ciclo completo dall'installazione alla configurazione in produzione in sei passaggi.

💡 Panoramica rapida:

  • Cosa fa: la libreria Dropzone.js trasforma qualsiasi elemento HTML in una zona di rilascio per file con invio AJAX al server.
  • Come installarla: un link CDN (unpkg o jsDelivr) oppure un pacchetto npm; entrambe le opzioni sono trattate nel primo passaggio.
  • Come gestirla lato server: uno script PHP riceve il file tramite $_FILES, esattamente come un form tradizionale; il terzo passaggio include codice pronto all'uso.
  • Dove applicarla: form di contatto con allegati, caricamento avatar, importazione massiva di immagini in una galleria WordPress.

Passaggio 1: installare Dropzone.js

Il modo più rapido è includere la libreria tramite CDN. Aggiungi questo tag al <head> della tua pagina:

1<script src="https://unpkg.com/dropzone@5/dist/min/dropzone.min.js"></script>

Il file CSS va incluso separatamente:

1<link rel="stylesheet" href="https://unpkg.com/dropzone@5/dist/min/dropzone.min.css" />

Se usi un bundler, installa via npm:

1npm install dropzone

E importalo nel tuo file JS:

1import Dropzone from "dropzone";

Nota che la versione 5.9 è l'ultima release stabile a inizio 2026. La versione sei è in stato beta e non ancora consigliata per l'uso in produzione. Il repository del progetto ora si trova su github.com/dropzone/dropzone (in precedenza enyo/dropzone).

Passaggio 2: markup HTML di base e inizializzazione

Crea un form con la classe dropzone. Dropzone lo individuerà automaticamente e lo trasformerà in una zona di rilascio:

1<form action="/upload.php"
2 class="dropzone"
3 id="my-dropzone"></form>

Tutto qui. Quando un utente trascina un file in questo form, la libreria lo invierà tramite una richiesta AJAX all'URL indicato nell'attributo action. Lato server, il file arriverà in $_FILES['file'], esattamente come con un invio tradizionale tramite <input type="file">.

Sviluppatore che scrive codice per caricamento file

Se hai bisogno di un fallback per browser senza JavaScript, aggiungi un campo tradizionale all'interno del form:

1<form action="/upload.php" class="dropzone">
2 <input name="file" type="file" multiple />
3</form>

Il pulsante «Scegli file» verrà mostrato solo quando JavaScript non è disponibile. Con JS attivo, Dropzone lo nasconderà e mostrerà la propria zona di rilascio.

Step 3: Gestire i file sul server

Uno script PHP riceve gli upload nello stesso modo di un form tradizionale. Ecco la versione minima funzionante:

1<?php
2if (!empty($_FILES)) {
3 $uploadDir = __DIR__ . '/uploads/';
4
5 if (!is_dir($uploadDir)) {
6 mkdir($uploadDir, 0755, true);
7 }
8
9 $originalName = basename($_FILES['file']['name']);
10 $targetPath = $uploadDir . time() . '_' . $originalName;
11
12 if (move_uploaded_file($_FILES['file']['tmp_name'], $targetPath)) {
13 http_response_code(200);
14 echo json_encode(['status' => 'ok', 'file' => $targetPath]);
15 } else {
16 http_response_code(500);
17 echo json_encode(['status' => 'error', 'message' => 'Failed to save file']);
18 }
19}

Cosa succede qui:

  • Lo script crea una cartella uploads/ se non esiste.
  • time() viene aggiunto al nome file originale, per prevenire conflitti di nomi.
  • basename() elimina qualsiasi tentativo di path traversal (tentativi di uscire dalla cartella tramite ../).
  • Dropzone si aspetta un HTTP 200 dal server; in caso contrario considera l'upload fallito e mostra un errore.

Per un uso in produzione, aggiungi la validazione dell'estensione e del MIME type:

1$allowed = ['jpg', 'jpeg', 'png', 'gif', 'pdf', 'zip'];
2$ext = strtolower(pathinfo($originalName, PATHINFO_EXTENSION));
3
4if (!in_array($ext, $allowed)) {
5 http_response_code(400);
6 echo json_encode(['status' => 'error', 'message' => 'Invalid file type']);
7 exit;
8}

Step 4: Limitare numero, dimensione e tipo di file

Le tre impostazioni di Dropzone più comunemente necessarie si configurano tramite una configurazione JavaScript:

1Dropzone.options.myDropzone = {
2 maxFiles: 10,
3 maxFilesize: 5, // In megabytes
4 acceptedFiles: 'image/*,.pdf',
5 dictDefaultMessage: 'Drag files here or click to select'
6};

Dettaglio delle opzioni:

  • maxFiles: quanti file l'utente può caricare in una volta. Di default non c'è limite; nell'esempio, non più di 10.
  • maxFilesize: dimensione massima di un singolo file in megabyte. Un valore di 5 significa 5 MB. Nelle versioni precedenti della libreria il default era 256 MB; nell'attuale 5.x è anch'esso 256, quindi specifica esplicitamente il tuo limite.
  • acceptedFiles: una stringa di MIME type separati da virgola. Il pattern image/* permette qualsiasi immagine, .pdf aggiunge i PDF all'elenco. Vedi la lista completa dei MIME type nella documentazione di Dropzone.
  • dictDefaultMessage: il testo che l'utente vede nella zona di upload vuota (in inglese in questo esempio).

La chiave myDropzone in Dropzone.options.myDropzone è la versione camelCase dell'attributo HTML id="my-dropzone". I trattini vengono rimossi e ogni parola successiva ha l'iniziale maiuscola: my-dropzonemyDropzone, file-upload-areafileUploadArea.

Le opzioni resizeWidth e resizeHeight permettono di ridimensionare le immagini lato client prima dell'invio. Questo fa risparmiare banda all'utente e riduce il carico sul server, specialmente quando i visitatori caricano foto dal telefono da 10-15 MB ciascuna. Specifica entrambi i parametri insieme:

1Dropzone.options.myDropzone = {
2 resizeWidth: 1200,
3 resizeHeight: 1200,
4 resizeQuality: 0.8,
5 resizeMethod: 'contain'
6};

Il metodo contain adatta l'immagine entro i limiti dati senza ritagliarla, mentre crop la ritaglia alle dimensioni esatte. Una qualità di 0.8 è praticamente indistinguibile dall'originale ma riduce la dimensione del file di 4-7 volte. Importante: il ridimensionamento funziona solo con file immagine; PDF e archivi vengono trasferiti così come sono. Il browser esegue il ridimensionamento tramite Canvas API, quindi non servono librerie aggiuntive.

Step 5: Invio manuale dei file

Di default, Dropzone invia un file al server subito dopo che è stato aggiunto. Questo non è sempre comodo: a volte serve lasciare che l'utente compili i campi del form accanto alla zona di upload e invii tutto con un unico pulsante.

Disabilita l'auto-upload con il flag autoProcessQueue: false e collega l'invio a un pulsante esterno:

1Dropzone.options.myDropzone = {
2 autoProcessQueue: false,
3 maxFilesize: 5,
4 init: function () {
5 const myDropzone = this;
6 const submitBtn = document.querySelector('#btnUpload');
7
8 submitBtn.addEventListener('click', function () {
9 if (myDropzone.getQueuedFiles().length === 0) {
10 alert('No files to upload');
11 return;
12 }
13 myDropzone.processQueue();
14 });
15
16 this.on('success', function (file, response) {
17 console.log('File uploaded:', file.name);
18 });
19 }
20};

Il punto chiave qui è il metodo processQueue(). Attiva l'invio di tutti i file accumulati in coda. L'evento success scatta per ogni singolo file, permettendoti di aggiornare l'interfaccia o salvare l'ID del file caricato in un campo nascosto del form.

Step 6: Inviare dati aggiuntivi insieme al file

A volte serve allegare parametri di servizio a un upload: ID utente, stato del documento, categoria selezionata. Dropzone mette a disposizione l'evento sending per questo:

1Dropzone.options.myDropzone = {
2 init: function () {
3 this.on('sending', function (file, xhr, formData) {
4 formData.append('status', 'draft');
5 formData.append('user_id', currentUserId);
6 });
7
8 this.on('success', function (file, responseText) {
9 console.log('Done:', responseText);
10 });
11 }
12};

L'evento sending viene chiamato subito prima che la XMLHttpRequest venga inviata. Il terzo argomento formData è un oggetto FormData standard a cui puoi aggiungere qualsiasi coppia chiave-valore tramite append(). Lato server, questi parametri vengono letti da $_POST['status'] e $_POST['user_id'].

Scenario pratico: l'utente seleziona un album da un menu a tendina accanto all'area di Dropzone, e l'ID dell'album viene inviato insieme a ogni file. Il server smista immediatamente gli upload nelle cartelle appropriate.

Sicurezza: tre regole che non puoi ignorare

Prima di mettere Dropzone in produzione, verifica tre aspetti.

Primo. Convalida sempre tipo e dimensione del file lato server, anche se hai già configurato acceptedFiles e maxFilesize in JS. Le restrizioni lato client possono essere aggirate in un minuto tramite la console del browser. Il filtraggio lato server è l'unica protezione reale.

Secondo. Archivia i file caricati fuori dalla document root o in una cartella con l'esecuzione di script disabilitata. Per esempio, questa struttura:

1/public_html ← document root ( index.php )
2/uploads ← folder with uploaded files (outside web access)

Quindi un URL diretto al file non funzionerà; il browser serve il file solo attraverso uno script PHP intermediario che verifica i permessi di accesso.

Terzo. Genera nomi file univoci sul server. Non fidarti del nome inviato dal client: potrebbe contenere ../, byte nulli o caratteri speciali. basename() + time() + una stringa casuale è il livello minimo.

Quarto. Verifica il contenuto del file, non solo l'estensione. Un utente malevolo può rinominare shell.php in photo.jpg e aggirare un filtro basato su pathinfo(). Un approccio affidabile: determina il MIME type effettivo tramite finfo_file() (il modulo PHP Fileinfo integrato) e confrontalo con una whitelist. Per le immagini, verifica anche che getimagesize() restituisca dimensioni valide; questo filtra file corrotti e script camuffati. Su progetti ad alto traffico, valuta di delegare gli upload a un microservizio separato dietro un reverse proxy che riceve lo stream, lo scansiona con un antivirus (ad esempio ClamAV) e solo dopo lo passa all'applicazione principale.

Il breve video dimostrativo qui sopra mostra l'intero processo in azione: markup HTML, gestore PHP e il risultato nel browser in tre minuti.

⁉️🤔 Domande frequenti

Dropzone.js funziona senza jQuery?

Sì, la libreria è completamente indipendente da jQuery. A partire dalla versione 5.x, tutte le dipendenze sono state rimosse; consulta la sezione Installazione su dropzone.dev. Detto questo, esiste un wrapper jQuery separato per chi preferisce usare $('.dropzone').dropzone(); si trova nello stesso pacchetto npm in dist/min/dropzone-jquery.min.js.

Come posso cambiare il testo di errore quando un file è troppo grande?

Usa l'opzione dictFileTooBig. Esempio: dictFileTooBig: 'File is too big. Maximum size is {{maxFilesize}} MB'. Il placeholder {{maxFilesize}} inserisce automaticamente il valore dalla tua configurazione. L'elenco completo delle chiavi dict per la localizzazione è nella documentazione.

I file possono essere caricati a blocchi per volumi grandi?

Sì, Dropzone v5 ha un supporto integrato per i caricamenti a blocchi tramite le opzioni chunking: true, chunkSize (in byte) e forceChunking. Il server deve essere in grado di riassemblare i blocchi: ricevere ogni pezzo con gli header dzchunkindex e dztotalchunkcount, salvarli temporaneamente e unirli dopo aver ricevuto l'ultimo. Un esempio PHP già pronto per l'assemblaggio dei blocchi è disponibile nel repository di Dropzone.

Come rimuovo l'anteprima di un file dopo un caricamento riuscito?

Chiama myDropzone.removeFile(file) nel gestore dell'evento success. Il file scomparirà dall'interfaccia ma rimarrà sul server. Se devi eliminarlo anche dal server, aggiungi una richiesta AJAX nello stesso gestore prima di removeFile.

Dropzone è compatibile con WordPress?

Sì, la libreria può essere inclusa tramite wp_enqueue_script() nel tuo tema o plugin. Uno scenario tipico in WordPress: sostituire il media uploader standard in una pagina admin personalizzata o in un form frontend. Devi solo specificare l'URL corretto del gestore, di solito admin_url('admin-ajax.php') con un'azione AJAX registrata.

Dropzone.js è adatto al tuo progetto

Se hai bisogno di caricamenti file drag-and-drop "qui e ora", Dropzone.js risolve il compito più velocemente di qualsiasi codice personalizzato. Ecco cinque motivi per cui la libreria resta rilevante nel 2026:

  • Nessuna dipendenza. Niente jQuery, Bootstrap o React. Includi un file JS e funziona.
  • Supporto cross-browser. Supporta browser fino a IE11 e degrada in modo elegante senza JavaScript.
  • Visualizzazione pronta all'uso. Anteprime delle immagini, barre di avanzamento, icone di successo/errore già pronte, senza lavoro di layout CSS.
  • Documentazione. Un GitBook aggiornato e un repository GitHub attivo (oltre 1.700 stelle nel 2026).
  • Flessibilità. Dal semplice ridimensionamento delle immagini prima del caricamento ai caricamenti a blocchi di file di gigabyte, tutto è configurabile.

Quando Dropzone non è la scelta migliore: se stai già usando React/Vue e vuoi un componente nativo senza manipolazione esterna del DOM. L'ecosistema React ha react-dropzone, Vue ha vue-dropzone. Ma per progetti vanilla JavaScript, jQuery e siti WordPress, Dropzone.js è uno strumento collaudato che non ti deluderà.

Esempio pratico di integrazione. Uno scenario tipico in WordPress: una pagina "Carica documenti" in un portale clienti. L'utente trascina le scansioni di un passaporto, un contratto e le foto di un immobile. Dropzone viene incluso tramite wp_enqueue_script() nel functions.php del tema, e il gestore è un'azione AJAX personalizzata registrata con wp_ajax_nopriv_. Ogni file caricato con successo viene aggiunto alla libreria media tramite wp_insert_attachment() e il suo ID viene salvato nei meta utente. L'intero codice frontend sta in 30 righe, il lato server in 50. Per confronto, un drag-and-drop personalizzato con le stesse capacità richiederebbe 400-600 righe di JavaScript e 2-3 giorni di sviluppo.

Quanto alle alternative: Uppy (dai creatori di Transloadit) offre un'architettura modulare con plugin per React, Svelte e caricamenti S3, ma richiede più dipendenze e configurazione. Fine Uploader non è più mantenuto dal 2018; esistono fork ma senza garanzie di sicurezza. Dropzone.js vince sul bilanciamento "funzionalità per unità di complessità": un file, configurazione minima e risultati visibili cinque minuti dopo l'integrazione.

Alcune capacità aggiuntive che vale la pena conoscere prima di iniziare. Dropzone fornisce un ciclo completo di eventi: addedfile, thumbnail, uploadprogress, complete e queuecomplete. L'ultimo è particolarmente utile: si attiva quando tutti i file in coda sono stati elaborati, permettendoti di mostrare all'utente un messaggio di riepilogo o reindirizzarlo a un'altra pagina. Per i guasti di rete, c'è un meccanismo di retry integrato: in caso di errore di caricamento, Dropzone mostra un pulsante "Riprova" accanto all'anteprima del file, e il retry passa attraverso lo stesso gestore senza duplicazione di codice. L'interfaccia è completamente localizzabile tramite le opzioni dict: dictCancelUpload, dictRemoveFile, dictMaxFilesExceeded e circa altre due dozzine di chiavi. Il supporto per la lingua italiana si configura in cinque minuti copiando un oggetto dalla documentazione. La libreria pesa 28 KB compressi (gzip), aggiungendo meno di 0,1 secondi al tempo di caricamento della pagina su mobile 4G. Tutte queste caratteristiche rendono Dropzone.js lo standard de facto per i caricamenti drag-and-drop nei progetti senza framework: un milione di download npm a settimana all'inizio del 2026, e utilizzo nei pannelli di amministrazione di WordPress, Laravel e Symfony.