
📤 Przesyłanie plików przez przeciąganie z Dropzone.js i PHP
Użytkownicy oczekują, że pliki na stronę można wrzucić, po prostu przeciągając je do okna przeglądarki. Przycisk „Wybierz plik" i eksplorator plików są już postrzegane jako zbędny ruch. Jednak napisanie drag-and-drop od zera w JavaScript to kilkaset linii kodu, paski postępu, podglądy i obsługa błędów. Na szczęście istnieje gotowa biblioteka, która zapewnia to wszystko od ręki.
Dropzone.js to open-source'owa biblioteka JavaScript do przesyłania plików przez przeciąganie. Wyświetla podgląd obrazów, pasek postępu i nie wymaga jQuery. Działa z dowolnym językiem serwerowym: PHP, Node.js, Python. W tym poradniku przedstawiam pełny cykl od instalacji po konfigurację produkcyjną w sześciu krokach.
💡 Szybki przegląd:
- Sedno sprawy: biblioteka Dropzone.js przekształca dowolny element HTML w strefę przeciągania plików z wysyłką AJAX na serwer.
- Jak zainstalować: link CDN (unpkg lub jsDelivr) albo pakiet npm, obie opcje omówiono w pierwszym kroku.
- Jak obsłużyć na serwerze: skrypt PHP odbiera plik przez
$_FILES, tak jak zwykły formularz, trzeci krok z gotowym kodem. - Gdzie zastosować: formularze kontaktowe z załącznikami, przesyłanie awatarów, masowy import obrazów do galerii WordPress.
Krok 1: Instalacja Dropzone.js
Najszybszym sposobem jest podłączenie biblioteki przez CDN. Proszę dodać ten tag w <head> strony:
1 <script src="https://unpkg.com/dropzone@5/dist/min/dropzone.min.js"></script>
Plik CSS podłącza się osobno:
1 <link rel="stylesheet" href="https://unpkg.com/dropzone@5/dist/min/dropzone.min.css" />
Jeśli korzystają Państwo z bundlera, proszę instalować przez npm:
1 npm install dropzone
I zaimportować w swoim pliku JS:
1 import Dropzone from "dropzone";
Proszę zwrócić uwagę: wersja 5.9 to ostatnia stabilna na początek 2026 roku. Szósta wersja ma status beta, na produkcję nie jest jeszcze zalecana. Repozytorium projektu znajduje się teraz pod adresem github.com/dropzone/dropzone (wcześniej było to enyo/dropzone).
Krok 2: Podstawowy szkielet HTML i inicjalizacja
Proszę utworzyć formularz z klasą dropzone. Dropzone automatycznie go znajdzie i przekształci w strefę przeciągania:
1 <form action="/upload.php" 2 class="dropzone" 3 id="my-dropzone"></form>
To wszystko. Gdy użytkownik przeciągnie plik do tego formularza, biblioteka wyśle go żądaniem AJAX pod adres z atrybutu action. Po stronie serwera plik trafi do $_FILES['file'], tak samo jak przy zwykłym wysyłaniu przez <input type="file">.

Jeśli potrzebny jest fallback dla przeglądarek bez JavaScript, proszę dodać wewnątrz formularza zwykłe pole:
1 <form action="/upload.php" class="dropzone"> 2 <input name="file" type="file" multiple /> 3 </form>
Przycisk „Wybierz plik" będzie widoczny tylko wtedy, gdy JavaScript jest niedostępny. Gdy JS jest włączony, Dropzone ukryje go i wyświetli swoją strefę przeciągania.
Krok 3: Obsługa plików na serwerze
Skrypt PHP przyjmuje przesłane pliki tak samo, jak ze zwykłego formularza. Minimalna działająca wersja:
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' => 'Не удалось сохранить файл']); 18 } 19 }
Co się tutaj dzieje:
- Skrypt tworzy folder
uploads/, jeśli go nie ma. - Do oryginalnej nazwy pliku dodawana jest wartość
time(), co zapobiega konfliktom nazw. basename()odcina potencjalne ataki path traversal (próby wyjścia poza folder przez../).- Dropzone oczekuje od serwera odpowiedzi HTTP 200, w przeciwnym razie uznaje wysyłkę za nieudaną i pokazuje błąd.
W wersji produkcyjnej proszę dodać jeszcze walidację rozszerzenia i typu 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' => 'Недопустимый тип файла']); 7 exit; 8 }
Krok 4: Ograniczenie liczby, rozmiaru i typu plików
Trzy najbardziej pożądane ustawienia Dropzone konfiguruje się przez obiekt konfiguracyjny JavaScript:
1 Dropzone.options.myDropzone = { 2 maxFiles: 10, 3 maxFilesize: 5, // В мегабайтах 4 acceptedFiles: 'image/*,.pdf', 5 dictDefaultMessage: 'Перетащите файлы сюда или кликните для выбора' 6 };
Omówienie opcji:
- maxFiles, ile plików użytkownik może przesłać jednorazowo. Domyślnie nie ma ograniczeń, w przykładzie: nie więcej niż 10.
- maxFilesize, maksymalny rozmiar pojedynczego pliku w megabajtach. Wartość 5 oznacza 5 MB. W starszych wersjach biblioteki domyślnie było to 256 MB, w aktualnej 5.x również 256, dlatego proszę jawnie podać swój limit.
- acceptedFiles, ciąg typów MIME oddzielonych przecinkami. Wzorzec
image/*dopuszcza dowolne obrazy,.pdfdodaje PDF do listy. Pełną listę typów MIME można znaleźć w dokumentacji Dropzone. - dictDefaultMessage, polski tekst, który użytkownik widzi w pustej strefie przesyłania.
Klucz myDropzone w Dropzone.options.myDropzone to wersja camelCase atrybutu HTML id="my-dropzone". Łączniki są odrzucane, każde kolejne słowo zaczyna się wielką literą: my-dropzone → myDropzone, file-upload-area → fileUploadArea.
Opcje resizeWidth i resizeHeight pozwalają zmniejszać obrazy po stronie klienta przed wysłaniem. Oszczędza to transfer danych użytkownika i odciąża serwer, szczególnie gdy odwiedzający przesyłają zdjęcia z telefonu po 10-15 MB każde. Proszę podawać oba parametry jednocześnie:
1 Dropzone.options.myDropzone = { 2 resizeWidth: 1200, 3 resizeHeight: 1200, 4 resizeQuality: 0.8, 5 resizeMethod: 'contain' 6 };
Metoda contain wpisuje obraz w zadane granice bez przycinania, crop przycina do dokładnych wymiarów. Jakość 0.8 jest w praktyce nie do odróżnienia od oryginału, a zmniejsza rozmiar pliku 4-7 razy. Ważne: zmiana rozmiaru działa tylko z plikami graficznymi, PDF-y i archiwa są przesyłane bez zmian. Przeglądarka wykonuje skalowanie przez Canvas API, dodatkowe biblioteki nie są potrzebne.
Krok 5: Ręczne wysyłanie plików
Domyślnie Dropzone wysyła plik na serwer natychmiast po dodaniu. Nie zawsze jest to wygodne: czasem trzeba pozwolić użytkownikowi wypełnić pola formularza obok strefy przesyłania, a następnie wysłać wszystko jednym przyciskiem.
Wyłączamy automatyczne wysyłanie flagą autoProcessQueue: false i podpinamy wysyłkę pod zewnętrzny przycisk:
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('Нет файлов для загрузки'); 11 return; 12 } 13 myDropzone.processQueue(); 14 }); 15 16 this.on('success', function (file, response) { 17 console.log('Файл загружен:', file.name); 18 }); 19 } 20 };
Kluczowym elementem jest tutaj metoda processQueue(). Uruchamia ona wysyłanie wszystkich plików zgromadzonych w kolejce. Zdarzenie success jest wywoływane dla każdego pliku osobno, można zaktualizować interfejs lub zapisać ID przesłanego pliku w ukrytym polu formularza.
Krok 6: Przekazywanie dodatkowych danych razem z plikiem
Czasami trzeba dołączyć do przesyłki parametry pomocnicze: ID użytkownika, status dokumentu, wybraną kategorię. W Dropzone służy do tego zdarzenie sending:
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('Готово:', responseText); 10 }); 11 } 12 };
Zdarzenie sending jest wywoływane bezpośrednio przed wysłaniem żądania XMLHttpRequest. Trzeci argument formData to standardowy obiekt FormData, do którego można dodać dowolne pary klucz-wartość przez append(). Po stronie serwera parametry te są odczytywane z $_POST['status'] i $_POST['user_id'].
Praktyczny scenariusz: użytkownik wybiera album z listy rozwijanej obok strefy Dropzone, a ID albumu jest przesyłane razem z każdym plikiem, serwer od razu rozmieszcza pliki w odpowiednich folderach.
Bezpieczeństwo: trzy zasady, których nie wolno ignorować
Zanim wdroży Pan/Pani Dropzone na produkcję, proszę sprawdzić trzy kwestie.
Po pierwsze. Zawsze proszę walidować typ i rozmiar pliku po stronie serwera, nawet jeśli skonfigurowano już acceptedFiles i maxFilesize w JS. Ograniczenia po stronie klienta można obejść w minutę przez konsolę przeglądarki. Filtr serwerowy to jedyna realna ochrona.
Po drugie. Proszę przechowywać przesłane pliki poza document root lub w folderze z wyłączonym wykonywaniem skryptów. Na przykład struktura:
1 /public_html ← document root ( index.php ) 2 /uploads ← папка с загруженными файлами (вне web-доступа)
Wtedy bezpośredni link do pliku nie zadziała, przeglądarka udostępnia plik wyłącznie przez pośredniczący skrypt PHP, który sprawdza uprawnienia dostępu.
Po trzecie. Generujcie Państwo unikalne nazwy plików na serwerze. Nie ufajcie nazwie przesłanej przez klienta: może zawierać ../, bajty zerowe lub znaki specjalne. basename() + time() + losowy ciąg, to absolutne minimum.
Po czwarte. Weryfikujcie Państwo zawartość pliku, a nie tylko rozszerzenie. Osoba atakująca może zmienić nazwę shell.php na photo.jpg i ominąć filtr oparty na pathinfo(). Niezawodny sposób: określić rzeczywisty typ MIME za pomocą finfo_file() (wbudowany moduł PHP Fileinfo) i porównać go z białą listą. W przypadku obrazów dodatkowo sprawdźcie, czy getimagesize() zwraca prawidłowe wymiary, odsieje to uszkodzone pliki i zamaskowane skrypty. W projektach o dużym obciążeniu warto wydzielić przesyłanie plików do osobnego mikroserwisu za reverse proxy, który przyjmuje strumień, skanuje go programem antywirusowym (np. ClamAV) i dopiero wtedy przekazuje do głównej aplikacji.
Krótkie wideo demonstracyjne powyżej pokazuje cały proces w działaniu: znaczniki HTML, skrypt obsługi PHP i rezultat w przeglądarce w ciągu trzech minut.
⁉️🤔 Często zadawane pytania
Czy Dropzone.js działa bez jQuery?
Tak, biblioteka jest całkowicie niezależna od jQuery. Począwszy od wersji 5.x usunięto wszystkie zależności, zob. sekcję Installation na dropzone.dev. Jednocześnie istnieje osobna nakładka jQuery dla osób przyzwyczajonych do
$('.dropzone').dropzone(), znajduje się ona w tym samym pakiecie npm, ścieżkadist/min/dropzone-jquery.min.js.
Jak zmienić tekst błędu, gdy plik jest za duży?
Proszę użyć opcji
dictFileTooBig. Przykład:dictFileTooBig: 'Файл слишком большой. Максимальный размер, {{maxFilesize}} МБ'. Szablon{{maxFilesize}}automatycznie wstawi wartość z Pana/Pani konfiguracji. Pełna lista kluczy dict do lokalizacji znajduje się w dokumentacji.
Czy można wysyłać pliki w częściach dla dużych wolumenów?
Tak, Dropzone v5 ma wbudowaną obsługę wysyłania fragmentarycznego (chunked upload) poprzez opcje
chunking: true,chunkSize(w bajtach) iforceChunking. Serwer musi przy tym umieć składać fragmenty: przyjmować każdą część z nagłówkamidzchunkindexidztotalchunkcount, tymczasowo je przechowywać i sklejać po otrzymaniu ostatniego. Dla PHP gotowy przykład składania fragmentów znajduje się w repozytorium Dropzone.
Jak usunąć podgląd pliku po pomyślnym wysłaniu?
Proszę wywołać
myDropzone.removeFile(file)w procedurze obsługi zdarzeniasuccess. Plik zniknie z interfejsu, ale pozostanie na serwerze. Jeśli trzeba usunąć go również z serwera, proszę dodać żądanie AJAX w tej samej procedurze obsługi przedremoveFile.
Czy Dropzone jest kompatybilny z WordPress?
Tak, bibliotekę można podłączyć przez
wp_enqueue_script()w motywie lub wtyczce. Typowy scenariusz dla WordPress: zastąpienie standardowego uploadera plików multimedialnych na niestandardowej stronie administracyjnej lub w formularzu frontendowym. Wystarczy wskazać poprawny URL handlera, zwykleadmin_url('admin-ajax.php')z zarejestrowaną akcją AJAX.
Czy warto wziąć Dropzone.js do swojego projektu
Jeśli potrzebują Państwo wysyłania plików przez przeciąganie i upuszczanie „tu i teraz", Dropzone.js zamyka zadanie szybciej niż jakikolwiek kod pisany samodzielnie. Pięć powodów, dla których biblioteka pozostaje aktualna w 2026 roku:
- Zero zależności. Ani jQuery, ani Bootstrap, ani React. Podłącza się jeden plik JS i działa.
- Kompatybilność międzyprzeglądarkowa. Wspiera przeglądarki aż do IE11, działa w trybie graceful degradation bez JavaScript.
- Gotowa wizualizacja. Podglądy obrazów, paski postępu, ikony sukcesu/błędu, od razu po wyjęciu z pudełka, bez tworzenia układu CSS.
- Dokumentacja. Aktualny GitBook i aktywne repozytorium na GitHub (1 700+ gwiazdek na moment 2026 roku).
- Elastyczność. Od prostej zmiany rozmiaru obrazów przed wysłaniem po fragmentaryczne wysyłanie gigabajtowych plików, wszystko konfiguruje się przez config.
Kiedy Dropzone nie jest najlepszym wyborem: jeśli używają już Państwo React/Vue i chcą natywny komponent bez zewnętrznych manipulacji DOM. W ekosystemie React istnieje react-dropzone, w Vue, vue-dropzone. Jednak dla czystego JavaScript, projektów jQuery i stron WordPress Dropzone.js to sprawdzone narzędzie, które nie zawiedzie.
Praktyczny przykład integracji. Jeden z typowych scenariuszy na stronie WordPress: strona „Prześlij dokumenty" w panelu klienta. Użytkownik przeciąga skany paszportu, umowy i zdjęcia obiektu. Dropzone podłącza się przez wp_enqueue_script() w functions.php motywu, handlerem jest niestandardowa akcja AJAX zarejestrowana przez wp_ajax_nopriv_. Każdy pomyślnie wysłany plik jest zapisywany w bibliotece mediów przez wp_insert_attachment(), a jego ID przechowywane w user meta. Cały kod frontendowy mieści się w 30 linijkach, część serwerowa w 50. Dla porównania: samodzielnie napisany drag-and-drop o tych samych możliwościach zająłby 400-600 linijek JavaScript i 2-3 dni pracy.
Jeśli chodzi o alternatywy: Uppy (od twórców Transloadit) oferuje modułową architekturę z wtyczkami dla React, Svelte i wysyłania do S3, ale wymaga więcej zależności i konfiguracji. Fine Uploader nie jest już wspierany od 2018 roku, forki istnieją, ale bez gwarancji bezpieczeństwa. Dropzone.js wygrywa bilansem „możliwości na jednostkę złożoności": jeden plik, minimum konfiguracji, rezultat widoczny po pięciu minutach od podłączenia.
Kilka dodatkowych możliwości, które warto znać przed startem. Dropzone udostępnia pełny cykl zdarzeń: addedfile, thumbnail, uploadprogress, complete i queuecomplete. To ostatnie jest szczególnie przydatne: uruchamia się, gdy wszystkie pliki z kolejki zostaną przetworzone, i można pokazać użytkownikowi podsumowujący komunikat lub przekierować na inną stronę. Na wypadek awarii sieci jest wbudowany retry: w razie błędu wysyłania Dropzone pokazuje przycisk „Powtórz" obok podglądu pliku, ponowne wysłanie odbywa się przez ten sam handler bez duplikowania kodu. Interfejs jest w pełni lokalizowalny przez słownik opcji dict: dictCancelUpload, dictRemoveFile, dictMaxFilesExceeded i jeszcze dwa tuziny kluczy, wsparcie dla języka polskiego robi się w pięć minut przez skopiowanie obiektu z dokumentacji. Biblioteka waży 28 KB w formie skompresowanej (gzip), co dodaje mniej niż 0,1 sekundy do ładowania strony na mobilnym 4G. Wszystkie te właściwości czynią Dropzone.js de facto standardem dla wysyłania drag-and-drop w projektach bez frameworków: milion pobrań npm tygodniowo według danych na początek 2026 roku, wykorzystanie w panelach administracyjnych WordPress, Laravel i Symfony.



