Експорт даних з веб-застосунку: реалізація під ключ

Коли дані веб-застосунку потрібно вивантажити у зручному форматі, синхронні механізми часто дають збій на великих обсягах. Ми реалізуємо експорт даних під ключ, забезпечуючи стабільну роботу навіть за значних навантажень. Наша команда бере на себе весь цикл — від проєктування до впровадження та подальшої підтримки, щоб ви отримували надійний інструмент, який масштабується разом із бізнесом.

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Послуги, які ми пропонуємо
Показано 1 з 1Усі 2062 послуг
Експорт даних з веб-застосунку: реалізація під ключ
Середній
~3-5 днів
Часті запитання

Експорт даних з веб-застосунку: реалізація під ключ

Згідно зі Статтею 20 GDPR, кожен суб'єкт даних має право отримати свої персональні дані у структурованому, загальновживаному та машиночитаному форматі. Але на практиці вивантаження даних на 500 000 рядків часто завалює сервер: тайм-аут 30 секунд і 502 Bad Gateway. Причина — синхронна архітектура та ліміти PHP. Ми вирішуємо цю проблему на рівні інфраструктури: асинхронна генерація через черги, потоковий запис у S3 та тимчасові signed URLs. Вимоги GDPR (ст. 20) зобов'язують надати персональні дані користувача у машиночитаному форматі — ми реалізуємо експорт під ключ. Зв'яжіться з нами для оцінки вашого проєкту.

Проблеми, які вирішуємо

  • N+1 запити при вивантаженні даних: при вивантаженні замовлень з позиціями кожен запит до БД плодить нові — для 10 000 замовлень це 10 001 запит. Використовуємо lazy loading через lazy(200) та жадібне завантаження зв'язків.
  • Тайм-аути та падіння пам'яті при синхронному вивантаженні >100 000 рядків: PHP-скрипт впирається у memory_limit та max_execution_time. Рішення — асинхронна обробка через черги з таймаутом 600 сек.
  • Гідратація невідповідності при SSR (hydration mismatch): якщо дані експорту вбудовуються на фронт, React/Nuxt можуть розсинхронізуватися. Експортуємо все через API з унікальними ID.

Чому асинхронний експорт кращий для великих даних?

Синхронний підхід підходить лише для малих обсягів (<10k рядків). При зростанні даних він веде до блокування інтерфейсу та скидання з'єднання. Асинхронна схема з чергою та повідомленнями знімає навантаження з HTTP-воркера та масштабується горизонтально. Коли один клієнт генерує звіт на 200k рядків, інші продовжують працювати без затримок.

Типовий приклад: один клієнт попросив вивантаження 150 000 замовлень у Excel. Спочатку розробник написав синхронний скрипт — на 40-й секунді PHP впав по memory_limit. Після переходу на чергу генерація зайняла 2 хвилини, користувач отримав листа з посиланням без блокувань.

Критерій Синхронний експорт Асинхронний експорт
Макс. обсяг до 10 000 рядків без обмежень
Блокування воркера так ні
Тайм-аут 30-60 сек 600+ сек
Повідомлення користувача ні (очікування) email / websocket
Робота з S3 ні так (потокове завантаження)
[User request] → [Create ExportJob record] → [Queue Worker]
↓
[Generate files]
↓
[Upload to S3 (zip)]
↓
[Email with download link] (signed URL, 7 days TTL)

Як ми це робимо: Laravel-реалізація

Для серверної частини використовуємо Laravel з PHP 8.3+. Ключовий патерн — сервіс DataExportService, який ставить задачу в чергу. Воркер ExportUserDataJob генерує файли профілю (JSON), замовлень (CSV з BOM), повідомлень (JSON) та пакує їх у ZIP з подальшим завантаженням у S3.

class DataExportService {
    public function exportUserData(User $user): Export {
        $export = Export::create([
            'user_id' => $user->id,
            'status' => 'pending',
            'expires_at' => now()->addDays(7),
        ]);

        ExportUserDataJob::dispatch($user, $export);

        return $export;
    }
}

class ExportUserDataJob implements ShouldQueue {
    public int $timeout = 600;

    public function __construct(private User $user, private Export $export) {}

    public function handle(): void {
        $this->export->update(['status' => 'processing']);

        $tempDir = sys_get_temp_dir() . '/export_' . $this->export->id;
        mkdir($tempDir, 0777, true);

        try {
            // Профіль
            file_put_contents(
                "$tempDir/profile.json",
                json_encode($this->exportProfile(), JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT)
            );

            // Замовлення
            $this->exportOrders("$tempDir/orders.csv");

            // Повідомлення
            $this->exportMessages("$tempDir/messages.json");

            // Створити ZIP
            $zipPath = sys_get_temp_dir() . "/export_{$this->export->id}.zip";
            $zip = new ZipArchive();
            $zip->open($zipPath, ZipArchive::CREATE);
            foreach (glob("$tempDir/*") as $file) {
                $zip->addFile($file, basename($file));
            }
            $zip->close();

            // Завантажити в S3
            $s3Key = "exports/{$this->user->id}/{$this->export->id}/data.zip";
            Storage::disk('s3')->put($s3Key, file_get_contents($zipPath));

            $this->export->update([
                'status' => 'ready',
                's3_key' => $s3Key,
            ]);

            $this->user->notify(new ExportReadyNotification($this->export));
        } finally {
            exec("rm -rf {$tempDir}");
            if (file_exists($zipPath ?? '')) unlink($zipPath);
        }
    }

    private function exportProfile(): array {
        return [
            'id' => $this->user->id,
            'name' => $this->user->name,
            'email' => $this->user->email,
            'created_at' => $this->user->created_at->toIso8601String(),
            'profile' => $this->user->profile?->toArray(),
        ];
    }

    private function exportOrders(string $path): void {
        $handle = fopen($path, 'w');
        fprintf($handle, chr(0xEF) . chr(0xBB) . chr(0xBF)); // UTF-8 BOM
        fputcsv($handle, ['Номер', 'Дата', 'Сума', 'Статус', 'Позиції'], ';');

        $this->user->orders()->with('items')->lazy(200)->each(function (Order $order) use ($handle) {
            $items = $order->items->map(fn($i) => "{$i->name} x{$i->quantity}")->join(', ');
            fputcsv($handle, [
                $order->number,
                $order->created_at->format('d.m.Y H:i'),
                number_format($order->total, 2),
                $order->status,
                $items,
            ], ';');
        });

        fclose($handle);
    }

    private function exportMessages(string $path): void {
        $messages = $this->user->messages()
            ->select('id', 'subject', 'body', 'created_at')
            ->get()
            ->map(fn($m) => [
                'id' => $m->id,
                'subject' => $m->subject,
                'body' => strip_tags($m->body),
                'date' => $m->created_at->toIso8601String(),
            ]);

        file_put_contents($path, json_encode($messages, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
    }

    public function failed(\Throwable $e): void {
        $this->export->update(['status' => 'failed']);
        Log::error('Export failed', ['export_id' => $this->export->id, 'error' => $e->getMessage()]);
    }
}

Як працює signed URL для завантаження?

Щоб не зберігати архіви вічно, використовуємо тимчасові посилання S3 з TTL 15 хвилин. Користувач отримує листа з унікальним посиланням, яке веде напряму в S3 — минаючи наш сервер.

public function download(Export $export): RedirectResponse
{
    $this->authorize('download', $export);

    if ($export->status !== 'ready' || $export->expires_at->isPast()) {
        abort(410, 'Експорт недоступний або застарів');
    }

    $url = Storage::disk('s3')->temporaryUrl($export->s3_key, now()->addMinutes(15));

    return redirect()->away($url);
}

Як підібрати формат експорту?

Вибір формату залежить від мети: для API та GDPR-вивантаження підходить JSON, для таблиць та Excel — CSV з BOM, для бухгалтерії — XLSX з форматуванням, для B2B інтеграцій — XML. Якщо потрібен архів кількох файлів, використовуємо ZIP.

Формат Застосування Інструмент
JSON API, GDPR-вивантаження PHP json_encode, Node JSON.stringify
CSV Таблиці, Excel fputcsv, csv-writer
XLSX Бухгалтерія, аналітика PhpSpreadsheet, ExcelJS
XML B2B інтеграції SimpleXML, xmlbuilder2
ZIP Архів кількох файлів ZipArchive, archiver (Node)

Процес роботи

  1. Аналітика — вивчаємо структуру даних, обсяги, вимоги до форматів.
  2. Проектування — обираємо стек (черга, сховище), проектуємо схему БД для експортів.
  3. Розробка — пишемо сервіс, воркери, контролери, тести.
  4. Тестування — навантажувальне тестування з реальними обсягами, перевірка тайм-аутів.
  5. Деплой — налаштовуємо черги (Redis, SQS), S3, cron для очищення.
  6. Документація — передаємо архітектурну документацію та інструкцію.

Що входить у роботу

  • Архітектурна документація з описом потоків даних.
  • Вихідний код з коментарями та строгою типізацією.
  • Інструкція з деплою з прикладами конфігурації черг та S3.
  • Навчання співробітників підтримки.
  • Гарантія 3 місяці на безперебійну роботу.

Терміни та вартість

Базовий експорт даних користувача (JSON + CSV у ZIP): 2–3 дні. З чергою, S3 та email-повідомленням: 3–4 дні. GDPR-сумісний експорт всіх персональних даних: 4–5 днів. Вартість розраховується індивідуально в залежності від обсягів та кількості форматів. Досвід нашої команди — 5+ років та 50+ проєктів з експорту даних. Замовте експорт для вашого проєкту — отримайте консультацію та оцінку протягом 1 дня.

Зв'яжіться з нами, щоб обговорити ваш проєкт та отримати точну оцінку.

Наші компетенції:

Останні роботи

  • Розробка сайту компанії B2B ADVANCE
    Розробка сайту компанії B2B ADVANCE
    1542
  • Розробка веб-додатків для компанії FEEDME
    Розробка веб-додатків для компанії FEEDME
    1393
  • Розробка веб-сайту для компанії БЕЛФІНГРУП
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    1081
  • Розробка інтернет магазину для компанії FURNORO
    Розробка інтернет магазину для компанії FURNORO
    1368
  • Розробка веб-додатків для компанії Enviok
    Розробка веб-додатків для компанії Enviok
    1139
  • Розробка веб-сайту для компанії ФІКСПЕР
    Розробка веб-сайту для компанії ФІКСПЕР
    1060