
📤 Upload de ficheiros com arrastar e largar usando Dropzone.js e PHP
Os utilizadores esperam carregar ficheiros para um site simplesmente arrastando-os para a janela do navegador. O botão «Escolher ficheiro» e o explorador de ficheiros já são vistos como passos desnecessários. Mas escrever a funcionalidade de arrastar e largar de raiz em JavaScript implica várias centenas de linhas de código, barras de progresso, pré-visualizações e tratamento de erros. Felizmente, existe uma biblioteca pronta a usar que cobre tudo isto de forma integrada.
O Dropzone.js é uma biblioteca JavaScript de código aberto para carregamento de ficheiros por arrastar e largar. Mostra pré-visualizações de imagens, uma barra de progresso e não requer jQuery. Funciona com qualquer linguagem do lado do servidor: PHP, Node.js, Python. Neste guia, encontrará o ciclo completo, da instalação à configuração de produção, em seis passos.
💡 Visão geral rápida:
- O que faz: a biblioteca Dropzone.js transforma qualquer elemento HTML numa zona de largar ficheiros, com envio AJAX para o servidor.
- Como instalar: um link CDN (unpkg ou jsDelivr) ou um pacote npm; ambas as opções são abordadas no primeiro passo.
- Como tratar no servidor: um script PHP recebe o ficheiro através de
$_FILES, tal como num formulário normal; o terceiro passo inclui código pronto a usar. - Onde aplicar: formulários de contacto com anexos, carregamento de avatares, importação em massa de imagens para uma galeria do WordPress.
Passo 1: Instalar o Dropzone.js
A forma mais rápida é incluir a biblioteca via CDN. Adicione esta tag ao <head> da sua página:
1 <script src="https://unpkg.com/dropzone@5/dist/min/dropzone.min.js"></script>
O ficheiro CSS é incluído separadamente:
1 <link rel="stylesheet" href="https://unpkg.com/dropzone@5/dist/min/dropzone.min.css" />
Se usar um bundler, instale via npm:
1 npm install dropzone
E importe-o no seu ficheiro JS:
1 import Dropzone from "dropzone";
Note que a versão 5.9 é a última versão estável disponível no início de 2026. A versão seis está em estado beta e ainda não é recomendada para produção. O repositório do projeto reside agora em github.com/dropzone/dropzone (anteriormente enyo/dropzone).
Passo 2: Marcação HTML básica e inicialização
Crie um formulário com a classe dropzone. O Dropzone irá encontrá-lo automaticamente e transformá-lo numa zona de largar ficheiros:
1 <form action="/upload.php" 2 class="dropzone" 3 id="my-dropzone"></form>
É tudo. Quando um utilizador arrasta um ficheiro para este formulário, a biblioteca envia-o através de um pedido AJAX para o URL no atributo action. Do lado do servidor, o ficheiro chegará em $_FILES['file'], exatamente como num envio normal através de <input type="file">.

Se precisar de uma alternativa para navegadores sem JavaScript, adicione um campo normal dentro do formulário:
1 <form action="/upload.php" class="dropzone"> 2 <input name="file" type="file" multiple /> 3 </form>
O botão «Choose file» só será mostrado quando o JavaScript não estiver disponível. Com o JS ativo, o Dropzone irá ocultá-lo e exibir a sua própria zona de arrastar e largar.
Passo 3: Processar ficheiros no servidor
Um script PHP recebe os uploads da mesma forma que um formulário normal. Aqui está a versão funcional mínima:
1 <?php 2 if (!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 }
O que acontece aqui:
- O script cria uma pasta
uploads/se esta não existir. time()é adicionado ao nome original do ficheiro, o que evita conflitos de nomes.basename()remove quaisquer ataques de path traversal (tentativas de sair da pasta através de../).- O Dropzone espera um HTTP 200 do servidor; caso contrário, considera o upload falhado e mostra um erro.
Para produção, adicione validação de extensão e tipo MIME:
1 $allowed = ['jpg', 'jpeg', 'png', 'gif', 'pdf', 'zip']; 2 $ext = strtolower(pathinfo($originalName, PATHINFO_EXTENSION)); 3 4 if (!in_array($ext, $allowed)) { 5 http_response_code(400); 6 echo json_encode(['status' => 'error', 'message' => 'Invalid file type']); 7 exit; 8 }
Passo 4: Limitar o número, tamanho e tipo de ficheiros
As três configurações do Dropzone mais frequentemente necessárias são definidas através de uma configuração JavaScript:
1 Dropzone.options.myDropzone = { 2 maxFiles: 10, 3 maxFilesize: 5, // In megabytes 4 acceptedFiles: 'image/*,.pdf', 5 dictDefaultMessage: 'Drag files here or click to select' 6 };
Descrição das opções:
- maxFiles: quantos ficheiros o utilizador pode enviar de uma vez. Por predefinição, não há limite; no exemplo, não mais de 10.
- maxFilesize: tamanho máximo de um único ficheiro em megabytes. Um valor de 5 significa 5 MB. Em versões mais antigas da biblioteca, o padrão era 256 MB; na versão atual 5.x também é 256, por isso especifique explicitamente o seu próprio limite.
- acceptedFiles: uma string de tipos MIME separados por vírgulas. O padrão
image/*permite quaisquer imagens,.pdfadiciona PDF à lista. Consulte a lista completa de tipos MIME na documentação do Dropzone. - dictDefaultMessage: o texto que o utilizador vê na zona de upload vazia (inglês neste exemplo).
A chave myDropzone em Dropzone.options.myDropzone é a versão em camelCase do atributo HTML id="my-dropzone". Os hífens são removidos e cada palavra subsequente é capitalizada: my-dropzone → myDropzone, file-upload-area → fileUploadArea.
As opções resizeWidth e resizeHeight permitem redimensionar imagens no cliente antes de enviar. Isto poupa largura de banda ao utilizador e reduz a carga do servidor, especialmente quando os visitantes enviam fotos de telemóvel de 10 a 15 MB cada. Especifique ambos os parâmetros em conjunto:
1 Dropzone.options.myDropzone = { 2 resizeWidth: 1200, 3 resizeHeight: 1200, 4 resizeQuality: 0.8, 5 resizeMethod: 'contain' 6 };
O método contain ajusta a imagem dentro dos limites fornecidos sem cortar, enquanto crop corta para as dimensões exatas. Uma qualidade de 0.8 é praticamente indistinguível do original, mas reduz o tamanho do ficheiro em 4 a 7 vezes. Importante: o redimensionamento só funciona com ficheiros de imagem; PDFs e arquivos são passados tal como estão. O navegador realiza o redimensionamento através da Canvas API, pelo que não são necessárias bibliotecas adicionais.
Passo 5: Submissão manual de ficheiros
Por predefinição, o Dropzone envia um ficheiro para o servidor imediatamente após ser adicionado. Isto nem sempre é conveniente: por vezes, precisa de deixar o utilizador preencher campos do formulário ao lado da zona de upload e submeter tudo com um único botão.
Desative o auto-upload com a flag autoProcessQueue: false e anexe a submissão a um botão externo:
1 Dropzone.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 };
O ponto-chave aqui é o método processQueue(). Ele aciona a submissão de todos os ficheiros acumulados na fila. O evento success é disparado para cada ficheiro individualmente, permitindo-lhe atualizar a interface ou guardar o ID do ficheiro enviado num campo oculto do formulário.
Passo 6: Enviar dados adicionais juntamente com o ficheiro
Por vezes, precisa de anexar parâmetros de serviço a um upload: ID do utilizador, estado do documento, categoria selecionada. O Dropzone tem o evento sending para isto:
1 Dropzone.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 };
O evento sending é chamado imediatamente antes de o XMLHttpRequest ser enviado. O terceiro argumento formData é um objeto FormData padrão ao qual pode adicionar quaisquer pares chave-valor através de append(). Do lado do servidor, estes parâmetros são lidos a partir de $_POST['status'] e $_POST['user_id'].
Cenário prático: o utilizador seleciona um álbum num menu suspenso ao lado da área do Dropzone, e o ID do álbum é enviado juntamente com cada ficheiro. O servidor organiza imediatamente os uploads nas pastas apropriadas.
Segurança: três regras que não pode ignorar
Antes de colocar o Dropzone em produção, verifique três coisas.
Primeira. Valide sempre o tipo e o tamanho do ficheiro do lado do servidor, mesmo que já tenha configurado acceptedFiles e maxFilesize no JS. As restrições do lado do cliente podem ser contornadas num minuto através da consola do navegador. A filtragem do lado do servidor é a única proteção real.
Segunda. Armazene os ficheiros enviados fora da raiz do documento ou numa pasta com a execução de scripts desativada. Por exemplo, esta estrutura:
1 /public_html ← document root ( index.php ) 2 /uploads ← folder with uploaded files (outside web access)
Nesse caso, um URL direto para o ficheiro não funcionará; o navegador serve o ficheiro apenas através de um script PHP intermediário que verifica as permissões de acesso.
Terceiro. Gere nomes de ficheiro únicos no servidor. Não confie no nome enviado pelo cliente: pode conter ../, bytes nulos ou caracteres especiais. basename() + time() + uma string aleatória é o nível mínimo.
Quarto. Verifique o conteúdo dos ficheiros, não apenas a extensão. Um atacante pode renomear shell.php para photo.jpg e contornar um filtro pathinfo(). Uma abordagem fiável: determine o tipo MIME real através de finfo_file() (o módulo Fileinfo integrado do PHP) e compare com uma lista de permissões. Para imagens, verifique adicionalmente se getimagesize() devolve dimensões válidas; isto filtra ficheiros corrompidos e scripts disfarçados. Em projetos de tráfego elevado, considere delegar os uploads para um microsserviço separado atrás de um proxy reverso que recebe o fluxo, analisa com antivírus (por exemplo, ClamAV) e só depois o passa para a aplicação principal.
O pequeno vídeo de demonstração acima mostra todo o processo em ação: marcação HTML, manipulador PHP e o resultado no navegador em três minutos.
⁉️🤔 Perguntas frequentes
O Dropzone.js funciona sem jQuery?
Sim, a biblioteca é completamente independente do jQuery. A partir da versão 5.x, todas as dependências foram removidas; veja a secção de Instalação em dropzone.dev. Dito isto, existe um wrapper jQuery separado para quem prefere
$('.dropzone').dropzone(); está no mesmo pacote npm, emdist/min/dropzone-jquery.min.js.
Como altero o texto de erro quando um ficheiro é demasiado grande?
Use a opção
dictFileTooBig. Exemplo:dictFileTooBig: 'File is too big. Maximum size is {{maxFilesize}} MB'. O placeholder{{maxFilesize}}insere automaticamente o valor da sua configuração. A lista completa de chaves dict para localização está na documentação.
Os ficheiros podem ser enviados em partes para grandes volumes?
Sim, o Dropzone v5 tem suporte integrado para uploads divididos em partes através das opções
chunking: true,chunkSize(em bytes) eforceChunking. O servidor deve ser capaz de reagrupar as partes: receber cada pedaço com os cabeçalhosdzchunkindexedztotalchunkcount, guardá-los temporariamente e fundi-los após receber o último. Um exemplo pronto em PHP para a montagem de partes está disponível no repositório do Dropzone.
Como removo a pré-visualização de um ficheiro após um upload bem-sucedido?
Chame
myDropzone.removeFile(file)no manipulador do eventosuccess. O ficheiro desaparecerá da interface, mas permanecerá no servidor. Se também precisar de o eliminar do servidor, adicione um pedido AJAX no mesmo manipulador antes deremoveFile.
O Dropzone é compatível com o WordPress?
Sim, a biblioteca pode ser incluída via
wp_enqueue_script()no seu tema ou plugin. Um cenário típico no WordPress: substituir o carregador de media padrão numa página de administração personalizada ou num formulário de frontend. Só precisa de especificar o URL do handler correto, normalmenteadmin_url('admin-ajax.php')com uma ação AJAX registada.
O Dropzone.js é adequado para o seu projeto
Se precisa de uploads de ficheiros por arrastar e largar «aqui e agora», o Dropzone.js resolve a tarefa mais rapidamente do que qualquer código personalizado. Aqui estão cinco razões pelas quais a biblioteca continua relevante em 2026:
- Sem dependências. Sem jQuery, Bootstrap ou React. Inclua um ficheiro JS e funciona.
- Suporte entre navegadores. Suporta navegadores até ao IE11 e degrada-se graciosamente sem JavaScript.
- Visualização pronta a usar. Pré-visualizações de imagem, barras de progresso, ícones de sucesso/erro de raiz, sem trabalho de layout CSS.
- Documentação. GitBook atualizado e um repositório GitHub ativo (mais de 1700 estrelas em 2026).
- Flexibilidade. Desde o simples redimensionamento de imagens antes do upload até uploads divididos em partes de ficheiros de gigabytes, tudo é configurável.
Quando o Dropzone não é a melhor escolha: se já está a usar React/Vue e quer um componente nativo sem manipulação externa do DOM. O ecossistema React tem o react-dropzone, o Vue tem o vue-dropzone. Mas para JavaScript vanilla, projetos jQuery e sites WordPress, o Dropzone.js é uma ferramenta comprovada que não o deixará ficar mal.
Exemplo prático de integração. Um cenário típico do WordPress: uma página «Carregar documentos» num portal de cliente. O utilizador arrasta digitalizações do passaporte, contrato e fotos da propriedade. O Dropzone é incluído via wp_enqueue_script() no functions.php do tema, e o handler é uma ação AJAX personalizada registada via wp_ajax_nopriv_. Cada ficheiro enviado com sucesso é adicionado à biblioteca de media via wp_insert_attachment(), e o seu ID é guardado nos metadados do utilizador. Todo o código de frontend cabe em 30 linhas, e o lado do servidor em 50. Para comparação, um arrastar e largar personalizado com as mesmas capacidades exigiria 400 a 600 linhas de JavaScript e 2 a 3 dias de desenvolvimento.
Quanto às alternativas: o Uppy (dos criadores do Transloadit) oferece uma arquitetura modular com plugins para React, Svelte e uploads para S3, mas requer mais dependências e configuração. O Fine Uploader não é mantido desde 2018; existem forks, mas sem garantias de segurança. O Dropzone.js ganha no equilíbrio de «funcionalidades por unidade de complexidade»: um ficheiro, configuração mínima e resultados visíveis cinco minutos após a integração.
Algumas capacidades adicionais que vale a pena conhecer antes de começar. O Dropzone fornece um ciclo completo de eventos: addedfile, thumbnail, uploadprogress, complete e queuecomplete. Este último é especialmente útil: é acionado quando todos os ficheiros na fila foram processados, permitindo-lhe mostrar ao utilizador uma mensagem de resumo ou redirecionar para outra página. Para falhas de rede, existe uma nova tentativa integrada: num erro de upload, o Dropzone exibe um botão «Tentar novamente» ao lado da pré-visualização do ficheiro, e a nova tentativa passa pelo mesmo handler sem duplicação de código. A interface é totalmente localizável através das opções dict: dictCancelUpload, dictRemoveFile, dictMaxFilesExceeded e cerca de duas dezenas de outras chaves. O suporte para português é configurado em cinco minutos, copiando um objeto da documentação. A biblioteca pesa 28 KB comprimidos (gzip), adicionando menos de 0,1 segundos ao tempo de carregamento da página em 4G móvel. Todas estas características fazem do Dropzone.js o padrão de facto para uploads por arrastar e largar em projetos sem frameworks: um milhão de downloads npm por semana no início de 2026, e utilização em painéis de administração WordPress, Laravel e Symfony.



