Efektywny import danych w Laravel – odczyt strumieniowy, chunkowanie i queue
1. Wprowadzenie
Efektywny import danych w Laravel – dlaczego to temat, który warto znać?
Współczesne aplikacje Laravel bardzo często muszą przetwarzać duże zbiory danych pochodzące z plików CSV, Excel, JSON lub XML. Przykłady są liczne:
- import tysięcy produktów do sklepu internetowego,
- synchronizacja użytkowników z systemu ERP,
- przetwarzanie raportów z platform finansowych,
- aktualizacja danych mieszkań, lokali i nieruchomości od deweloperów.
Na pierwszy rzut oka może się wydawać, że wystarczy „przelecieć” plik pętlą foreach i wstawić dane do bazy. Niestety – to podejście działa tylko dla małych plików. Gdy pojawia się plik ważący 200 MB, a w nim 1 000 000 rekordów — Laravel może się „zadławić”:
- Przekroczenie limitu pamięci PHP (
Allowed memory size...) - Przekroczenie czasu wykonania (
Maximum execution time...) - Zawieszony worker lub timeout podczas przetwarzania
- Użytkownik, który nie wie, czy import się zakończył
To nie tylko problem techniczny — to również problem UX i kosztów serwera.
Co znajdziesz w tym artykule?
W tym przewodniku pokażę Ci wydajne i odporne na błędy podejścia do importu dużych plików w Laravel:
- jak przetwarzać pliki liniowo (streaming),
- jak je dzielić na mniejsze paczki (chunkowanie),
- jak zastosować kolejki i batch processing (queue, jobs, batch).
Wszystko z praktycznymi przykładami kodu, które można od razu wdrożyć w swoim projekcie.
Kiedy ten artykuł Ci się przyda?
- Masz plik CSV od klienta i chcesz zaimportować dane do bazy
- Przetwarzasz duże XML/JSON z systemu zewnętrznego
- Budujesz aplikację SaaS i chcesz umożliwić użytkownikom import danych
- Nie chcesz zawieszać serwera przy każdym większym pliku
W kolejnym punkcie pokażę, czego NIE robić – czyli jakich podejść unikać, jeśli zależy Ci na stabilności i wydajności aplikacji.
2. Pułapki – czego nie robić
Zanim przejdziemy do dobrych praktyk i rozwiązań, warto omówić kilka często popełnianych błędów, które pojawiają się podczas pracy z dużymi plikami w Laravelu. Te pułapki mogą prowadzić do poważnych problemów ze stabilnością aplikacji i znacznym zużyciem zasobów serwera.
❌ file() i Storage::get()
Te funkcje ładują cały plik do pamięci RAM, co jest całkowicie nieopłacalne (i niebezpieczne) przy większych plikach:
$lines = file(storage_path('app/import.csv'));
Albo:
$content = Storage::get('import.csv');
$lines = explode("\n", $content);
🔴 Problem: Nawet plik 200 MB potrafi zająć kilka gigabajtów pamięci, zwłaszcza po rozbiciu go na linie i dalszym przetwarzaniu.
❌ Zbyt długa pętla foreach bez chunkowania
Często spotykany błąd:
foreach ($lines as $line) {
// parsowanie, walidacja, insert
}
🔴 Problem: brak limitowania, brak monitoringu, a także brak możliwości wznowienia przetwarzania w razie błędu. To prosta droga do przeciążenia serwera.
❌ Bezpośredni import do bazy bez transakcji i kolejek
Wielu początkujących programistów próbuje natychmiastowo wstawiać dane do bazy:
DB::table('products')->insert($parsedRow);
🔴 Problem: Przy importach powyżej kilku tysięcy rekordów zaczynają się:
- timeouty,
- błędy blokad,
- konflikty transakcyjne,
- spadki wydajności całej aplikacji.
❌ Brak komunikacji z użytkownikiem
Nie tylko backend cierpi. Często użytkownik:
- nie wie, czy import się rozpoczął,
- nie otrzymuje powiadomienia po zakończeniu,
- nie ma dostępu do raportu błędów lub logów.
To zły UX i potencjalne źródło frustracji.
✅ Co zamiast tego?
W kolejnych częściach artykułu pokażemy:
- jak wykorzystać strumieniowe przetwarzanie danych,
- jak używać chunkowania i kolejek,
- jak skalować i zabezpieczać cały proces importu.
Przejdźmy teraz do konkretów – zacznijmy od strumieniowego odczytu plików!
3. Techniki przetwarzania dużych plików w Laravel – streaming, chunking, queue
W tej części skupimy się na trzech głównych podejściach do wydajnego przetwarzania dużych plików w Laravel:
- strumieniowanie danych (streaming),
- dzielenie danych na paczki (chunking),
- przetwarzanie asynchroniczne za pomocą kolejek (queue, jobs, batch).
Każde z tych rozwiązań ma swoje miejsce i zalety, a często najlepszym podejściem jest łączenie ich ze sobą.
3.1. Streaming – odczyt danych linia po linii
Streaming pozwala przetwarzać plik bez ładowania go całkowicie do pamięci RAM. Idealne dla dużych plików tekstowych, CSV, TSV.
📌 Przykład: streaming pliku CSV z dysku lokalnego
use Illuminate\Support\Facades\Storage;
$handle = Storage::readStream('import.csv');
while (($line = fgets($handle)) !== false) {
$columns = str_getcsv($line);
// przetwarzaj $columns
}
fclose($handle);
To podejście jest lekkie, stabilne i wyjątkowo odporne na duże rozmiary pliku.
✅ Zastosowania:
- import plików powyżej 100 MB,
- przetwarzanie rekordów 1:1,
- ograniczony dostęp do zasobów serwera.
3.2. Chunking – dzielenie danych na paczki
Chunking polega na przetwarzaniu danych w ustalonych porcjach. Laravel oferuje LazyCollection, które idealnie nadają się do tego zadania.
📌 Przykład: chunkowanie streamu
use Illuminate\Support\LazyCollection;
LazyCollection::make(function () {
$handle = fopen(storage_path('app/import.csv'), 'r');
while (($line = fgets($handle)) !== false) {
yield str_getcsv($line);
}
fclose($handle);
})
->chunk(1000)
->each(function ($chunk) {
foreach ($chunk as $row) {
// przetwarzanie rekordu
}
});
✅ Zastosowania:
- walidacja i przygotowanie danych,
- grupowe zapisy do bazy danych,
- redukcja liczby zapytań INSERT.
3.3. Kolejki – asynchroniczne przetwarzanie danych
Zamiast przetwarzać wszystko od razu, możemy delegować każdy rekord lub każdą paczkę danych do osobnego zadania (Job) i wykonać je w tle.
📌 Przykład: dispatchowanie do kolejki
foreach ($rows as $row) {
ImportCsvRow::dispatch($row);
}
ImportCsvRow.php
class ImportCsvRow implements ShouldQueue
{
public function __construct(public array $row) {}
public function handle()
{
// Walidacja i zapis do DB
}
}
✅ Zastosowania:
- import danych bez obciążania requestu,
- retry przy błędach,
- możliwość śledzenia postępu i logowania.
W kolejnej części pokażemy realne scenariusze i przykłady, np. jak zaimportować 1 milion rekordów CSV z wykorzystaniem tych podejść razem.
4. Praktyczne przypadki importu danych w Laravel
W tej części przedstawimy trzy realistyczne scenariusze, które często występują w aplikacjach opartych o Laravel. Każdy przypadek pokazuje, jak dobrać odpowiednie techniki opisane wcześniej: streaming, chunking i kolejki.
🧾 Przypadek 1: Import pliku CSV z 1 milionem produktów
Sytuacja: klient przesyła plik CSV z danymi produktów (ID, nazwa, opis, cena, stan magazynowy, kategoria).
Rozwiązanie:
- Użycie
Storage::readStream()+fgets()do odczytu linii, - Każdy rekord przekazywany do kolejki jako osobne zadanie (Job),
- Walidacja danych i zapis do bazy w tle,
- Logowanie błędnych rekordów do osobnego pliku lub tabeli.
Bonus: dodanie postępu przetwarzania (np. przez Redis lub DB counter).
📄 Przypadek 2: Przetwarzanie dużego pliku XML z systemu deweloperskiego
Sytuacja: plik XML z danymi mieszkań/lokali o rozmiarze 300 MB zawiera zagnieżdżoną strukturę z property, building, unit itd.
Rozwiązanie:
- Użycie natywnej klasy PHP
XMLReader(działa jak streaming), - Iteracja przez
while ($reader->read())tylko po wybranych nodach, - Parsowanie danych i wrzucanie ich do kolejki batchowanej (grupy rekordów),
- Przekształcenie danych do formatu CPT (np. wtyczka WordPressa przez REST API).
Zaleta: minimalne zużycie pamięci, niezależnie od rozmiaru pliku.
📊 Przypadek 3: Import pliku Excel (.xlsx) od użytkownika SaaS
Sytuacja: użytkownik wrzuca przez frontend plik .xlsx z danymi klientów (imię, nazwisko, e-mail, numer telefonu).
Rozwiązanie:
- Użycie biblioteki Laravel Excel (maatwebsite/excel),
- Wczytywanie danych z
ToCollectionzamiastToModel(więcej kontroli), - Podzielenie danych na chunk w pamięci i wysyłanie ich do kolejki,
- Wysyłka maila po zakończeniu przetwarzania z podsumowaniem i błędami.
public function collection(Collection $rows)
{
$rows->chunk(100)->each(function ($chunk) {
ImportCustomersJob::dispatch($chunk);
});
}
Wskazówka: zawsze limituj rozmiar uploadu i dodaj walidację struktury.
W kolejnej części zaprezentujemy konkretne narzędzia i biblioteki, które ułatwiają cały proces importu.
5. Narzędzia i biblioteki warte uwagi
Aby efektywnie przetwarzać duże pliki w Laravelu, warto skorzystać z wyspecjalizowanych narzędzi i bibliotek. W tej sekcji prezentujemy te, które najlepiej sprawdzają się w pracy z plikami CSV, XML, Excel oraz w zarządzaniu kolejkami.
📦 Laravel Excel (maatwebsite/excel)
Zastosowanie: Import/eksport plików Excel (.xlsx, .xls, .csv) z dużą elastycznością.
Zalety:
- obsługa kolejek,
- import chunkowany,
- wysoka integracja z Eloquent,
- walidacja i mapowanie danych.
composer require maatwebsite/excel
Uwaga: Dla bardzo dużych plików lepsze może być prostsze podejście (np. fopen()), bo Laravel Excel tworzy bufor w pamięci.
📦 Spatie Simple Excel
Zastosowanie: ultralekki parser i zapis plików CSV bez zależności zewnętrznych.
composer require spatie/simple-excel
Zalety:
- obsługuje streaming,
- niski narzut pamięci,
- idealny do prostych struktur CSV,
- działa z
LazyCollection.
use Spatie\SimpleExcel\SimpleExcelReader;
SimpleExcelReader::create(storage_path('app/import.csv'))
->getRows()
->each(function(array $row) {
dispatch(new ImportCsvRow($row));
});
📦 League CSV
Zastosowanie: czytanie/zapisywanie CSV z kontrolą delimitera, nagłówków, walidacji.
Zalety:
- bardzo elastyczna konfiguracja,
- strumieniowy zapis,
- stabilna dokumentacja,
- zgodność z PSR.
composer require league/csv
📦 XMLReader (natywne PHP)
Zastosowanie: streamingowe odczytywanie dużych plików XML, idealne do danych z ERP lub systemów deweloperskich.
Zalety:
- bardzo szybki,
- nie ładuje całego drzewa XML do pamięci,
- minimalne zużycie zasobów.
$reader = new \XMLReader();
$reader->open(storage_path('app/oferty.xml'));
while ($reader->read()) {
if ($reader->nodeType === XMLReader::ELEMENT && $reader->name === 'local') {
$xml = $reader->readOuterXML();
// przetwórz lokal
}
}
📦 Laravel Horizon
Zastosowanie: monitorowanie i zarządzanie kolejkami Laravel w czasie rzeczywistym.
Zalety:
- interfejs webowy,
- śledzenie błędów i retry,
- obsługa batchy,
- priorytety i limity przetwarzania.
composer require laravel/horizon
php artisan horizon:install
Można łatwo połączyć go z Redisem i ustawić osobne kolejki do importów:
ImportCsvRow::dispatch($data)->onQueue('imports');
W kolejnym rozdziale pokażemy, jak optymalizować przetwarzanie pod kątem wydajności: limity pamięci, timeouty, priorytety zadań i skalowanie.
6. Wydajność i optymalizacja przetwarzania dużych plików
Przy przetwarzaniu plików ważących setki megabajtów, nawet najlepszy kod nie pomoże, jeśli środowisko nie jest odpowiednio skonfigurowane. W tej sekcji omówimy praktyczne aspekty optymalizacji importu danych w Laravelu – od ustawień PHP, przez konfigurację kolejek, aż po skalowanie i monitoring.
🛠️ 6.1. Ustawienia PHP i środowiska
memory_limit
Zwiększ limit pamięci w php.ini lub .env (jeśli używasz Laravel Sail lub Docker):
memory_limit=512M
max_execution_time
Dla importów uruchamianych z CLI możesz ustawić:
max_execution_time=0
co oznacza brak limitu czasu wykonania.
Obsługa błędów
Zawsze zabezpieczaj import try/catch i loguj błędy, np. do osobnej tabeli import_errors lub pliku w storage/logs/import.log.
⚙️ 6.2. Konfiguracja kolejek
- Stosuj osobne kolejki dla importu danych (np.
imports,high,low), - Ustaw limity czasu (
timeout) i prób (tries) w klasach Job:
public $timeout = 120;
public $tries = 3;
- Warto również ograniczyć liczbę zadań przetwarzanych jednocześnie:
php artisan queue:work --queue=imports --max-jobs=100
🧪 6.3. Batch processing i Horizon
Laravel Horizon pozwala tworzyć batch’e zadań i monitorować ich stan. To świetne rozwiązanie, jeśli chcesz:
- wiedzieć, czy cały import się zakończył,
- wysłać powiadomienie po sukcesie lub błędzie,
- zatrzymać przetwarzanie przy przekroczeniu limitu błędów.
Bus::batch([
new ImportCsvRow($chunk1),
new ImportCsvRow($chunk2),
])->then(function () {
// sukces
})->catch(function () {
// błąd
})->dispatch();
📈 6.4. Skalowanie i monitoring
Redis i więcej workerów:
- Używaj Redis jako backend dla kolejek,
- Ustal ilość workerów zależnie od CPU i pamięci RAM:
php artisan horizon
Monitorowanie:
- Zintegrowany Horizon UI,
- Możliwość podłączenia do zewnętrznych systemów (Sentry, Slack, e-mail),
- Śledzenie czasu przetwarzania, liczby błędów, opóźnień.
W kolejnym punkcie skupimy się na doświadczeniu użytkownika – jak poinformować go, że import trwa i zakończył się sukcesem.
7. UX i SEO – jak informować użytkownika o postępie importu danych
Wydajne przetwarzanie plików po stronie serwera to jedno – równie istotne jest to, co widzi i odczuwa użytkownik końcowy. W tej części skupimy się na dobrych praktykach UX w kontekście importu danych oraz aspektach SEO związanych z importem przez frontend.
👀 7.1. Powiadomienia o statusie importu
Użytkownik powinien wiedzieć:
- czy import się rozpoczął,
- czy trwa,
- czy zakończył się sukcesem lub błędem,
- ile rekordów zostało przetworzonych.
Rozwiązania:
- Alerty frontendowe: z użyciem Inertia.js / Vue / React,
- Powiadomienia e-mail/SMS po zakończeniu importu,
- Webhooki lub eventy WebSocket (Laravel Echo + Pusher lub Soketi),
- Polling (np. AJAX co 5s pytający o status
GET /import-status).
📝 7.2. Raporty z przetwarzania danych
Po zakończeniu importu warto wygenerować raport:
- liczba poprawnie przetworzonych rekordów,
- liczba błędnych rekordów,
- lista błędów z liniami i przyczyną (np. z walidacji),
- czas przetwarzania.
Można to zapisać w bazie lub jako plik CSV do pobrania.
💡 7.3. Obsługa importu w UI
W widoku aplikacji można:
- pokazać pasek postępu (np. oparty o % batchu wykonanego),
- pokazać spinner + komunikat „Import trwa…”
- zablokować przycisk ponownego wysłania,
- umożliwić podgląd statusu i historii importów użytkownika.
🔎 7.4. Czy import ma znaczenie dla SEO?
Zazwyczaj nie – import danych to część backendu. Ale są wyjątki:
- Jeśli dane są używane do generowania widoków (np. katalog produktów), to szybkość ich zaimportowania wpływa na:
- dostępność nowych podstron,
- obecność danych w sitemapie,
- ranking Google w przypadku błędnych/niepełnych danych.
Wniosek: nawet backendowy import może mieć wpływ na SEO, jeśli od jego poprawności zależy to, co widzi robot Google.
W ostatnim rozdziale podsumujemy poznane techniki i wskażemy najlepsze podejścia w zależności od scenariusza.
8. Podsumowanie – które podejście kiedy wybrać?
W tym artykule przedstawiliśmy szereg technik oraz narzędzi, które pozwalają na bezpieczne i wydajne przetwarzanie dużych plików w aplikacjach Laravel. Zakończmy podsumowaniem, które ułatwi Ci decyzję, kiedy i czego używać.
✅ Jeśli masz mały plik CSV (<5MB):
- Możesz użyć Laravel Excel lub nawet
file()/Storage::get()– ale z umiarem. - Dobrym wyborem będzie też
SimpleExcelReaderz bezpośrednim importem do bazy.
✅ Jeśli przetwarzasz średni plik CSV (10–100 MB):
- Użyj
Storage::readStream()+fgets()lubSimpleExcelReader - Dane przetwarzaj od razu albo kieruj do kolejki (dla większego bezpieczeństwa).
- Dodaj
try/catchi raportowanie błędów.
✅ Jeśli masz bardzo duży plik CSV/XML (>100 MB, >100k rekordów):
- Stosuj streaming + chunking + kolejki,
- Dziel dane przy pomocy
LazyCollectionlubXMLReader, - Zrób batchowanie i przetwarzanie asynchroniczne,
- Monitoruj postęp za pomocą Laravel Horizon,
- Zadbaj o powiadomienia i UX użytkownika (mail, status, raport).
✅ Jeśli użytkownik wrzuca plik przez UI:
- Waliduj plik i jego strukturę,
- Ogranicz rozmiar uploadu,
- Obsługuj import przez kolejki,
- Poinformuj użytkownika o postępie i zakończeniu importu,
- Przechowuj historię i błędy importów.
📌 Rekomendacje końcowe
- Zawsze testuj importy na danych testowych (np. 1k / 10k / 100k rekordów).
- Unikaj przetwarzania wszystkiego w jednej pętli – nawet jeśli działa lokalnie.
- Loguj błędy, informuj użytkownika i przewiduj wyjątki.
- Korzystaj z kolejek, Horizon i batchy – Laravel daje wszystko, czego potrzebujesz.
Dobrze zaprojektowany mechanizm importu danych nie tylko chroni Twój serwer przed przeciążeniem, ale też daje użytkownikowi komfort i pewność, że jego dane są bezpieczne.
Dziękuję za lekturę! Jeśli chcesz rozszerzyć ten temat o eksport danych, synchronizację z zewnętrznymi API lub porównanie bibliotek, daj znać – to świetna baza do kolejnych artykułów!
Dodaj komentarz