ייצוא נתונים מיישומי אינטרנט: הטמעה במפתח פתוח

כאשר יש צורך לייצא נתונים מאפליקציית web בפורמט נוח, מנגנונים סינכרוניים נכשלים לעיתים קרובות בנפחים גדולים. אנו מיישמים ייצוא נתונים במפתח מלא, ומבטיחים פעילות יציבה גם תחת עומסים משמעותיים. הצוות שלנו מטפל בכל המחזור—מתכנון ועד יישום ותמיכה שוטפת—כך שתקבלו כלי אמין שגדל עם העסק שלכם.

פיתוח ותחזוקה של כל סוגי האתרים:

אתרי מידע או יישומי אינטרנט
אתרי תדמית, דפי נחיתה, אתרי חברה, קטלוגים מקוונים, חידונים, אתרי קידום, בלוגים, מקורות חדשות, פורטלי מידע, פורומים, אגרגטורים
אתרי מסחר אלקטרוני או יישומי אינטרנט
חנויות מקוונות, פורטלי B2B, שווקים, בורסות מקוונות, אתרי קאשבק, בורסות, פלטפורמות דרופשיפינג, מנתחי מוצרים
יישומי אינטרנט לניהול תהליכים עסקיים
מערכות CRM, מערכות ERP, פורטלים ארגוניים, מערכות ניהול ייצור, מנתחי מידע
אתרי שירות אלקטרוני או יישומי אינטרנט
פלטפורמות מודעות, בתי ספר מקוונים, בתי קולנוע מקוונים, בוני אתרים, פורטלים לשירותים אלקטרוניים, פלטפורמות אירוח וידאו, פורטלים נושאיים

אלה רק חלק מהסוגים הטכניים של אתרים שאנו עובדים איתם, ולכל אחד מהם יכולים להיות מאפיינים ופונקציונליות ספציפיים משלו, וכן ניתן להתאים אותם לצרכים ולמטרות הספציפיים של הלקוח.

השירותים שאנו מציעים
מציג 1 מתוך 1כל 2062 השירותים
ייצוא נתונים מיישומי אינטרנט: הטמעה במפתח פתוח
בינוני
~3-5 ימים

הכישורים שלנו:

שאלות נפוצות

העבודות האחרונות

  • פיתוח אתר חברה B2B ADVANCE
    פיתוח אתר חברה B2B ADVANCE
    1502
  • פיתוח אפליקציית ווב עבור FEEDME
    פיתוח אפליקציית ווב עבור FEEDME
    1344
  • פיתוח אתר עבור BELFINGROUP
    פיתוח אתר עבור BELFINGROUP
    1052
  • פיתוח חנות מקוונת לחברת FURNORO
    פיתוח חנות מקוונת לחברת FURNORO
    1307
  • פיתוח אפליקציית ווב עבור Enviok
    פיתוח אפליקציית ווב עבור Enviok
    1050
  • פיתוח אתר לחברת FIXPER
    פיתוח אתר לחברת FIXPER
    1033

ייצוא נתונים מיישומי אינטרנט: הטמעה במפתח סגור

על פי סעיף 20 של ה-GDPR, לכל נושא נתונים יש זכות לקבל את נתוניו האישיים בפורמט מובנה, נפוץ וקריא במכונה.

אבל בפועל, ייצוא של 500,000 שורות לעיתים קרובות מפיל את השרת: פסק זמן של 30 שניות ו-502 Bad Gateway. הסיבה היא ארכיטקטורה סינכרונית ומגבלות PHP. אנו פותרים זאת ברמת התשתית: יצירה אסינכרונית באמצעות תורים, כתיבה בזרימה ל-S3, ו-כתובות URL חתומות זמניות. הדרישות לפי GDPR (סעיף 20) מחייבות מתן נתוני משתמש אישיים בפורמט קריא במכונה — אנו מיישמים ייצוא במפתח סגור. צרו קשר להערכת הפרויקט שלכם.

בעיות שאנו פותרים

  • שאילתות N+1 במהלך ייצוא נתונים: בעת ייצוא הזמנות עם פריטים, כל שאילתת מסד נתונים יוצרת שאילתות חדשות — עבור 10,000 הזמנות זה 10,001 שאילתות. אנו משתמשים בטעינה עצלה באמצעות lazy(200) וטעינה מוקדמת של קשרים.
  • פסקי זמן ודלדול זיכרון עם ייצוא סינכרוני של יותר מ-100,000 שורות: סקריפט ה-PHP נתקל ב-memory_limit וב-max_execution_time. הפתרון שלנו הוא עיבוד אסינכרוני באמצעות תורים עם פסק זמן של 600 שניות.
  • אי-התאמה בהידרציה במהלך SSR: אם נתוני ייצוא מוטמעים בצד הלקוח, React/Nuxt עלולים לאבד סנכרון. אנו מייצאים הכל דרך API עם מזהים ייחודיים.

מדוע ייצוא אסינכרוני עדיף לנתונים גדולים

ייצוא סינכרוני עובד רק לנפחים קטנים (<10k שורות). ככל שהנתונים גדלים, זה מוביל לחסימת ממשק ולניתוקי חיבור. תכנית אסינכרונית עם תור והתראות מסירה עומס מעובד ה-HTTP ומתרחבת אופקית. כאשר לקוח אחד מייצר דוח של 200k שורות, אחרים ממשיכים לעבוד ללא עיכובים.

דוגמה טיפוסית: לקוח ביקש ייצוא של 150,000 הזמנות ל-Excel. בתחילה, המפתח כתב סקריפט סינכרוני — ב-40 שניות, PHP קרס עקב memory_limit. לאחר מעבר לתור, הייצור ארך 2 דקות, והמשתמש קיבל דוא"ל עם קישור ללא כל חסימה.

קריטריון ייצוא סינכרוני ייצוא אסינכרוני
נפח מקסימלי עד 10,000 שורות ללא הגבלה
חסימת עובד כן לא
פסק זמן 30–60 שניות 600+ שניות
התראה למשתמש לא (המתנה) דוא"ל / 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+ לשרת. התבנית המרכזית היא [User request] → [Create ExportJob record] → [Queue Worker] ↓ [Generate files] ↓ [Upload to S3 (zip)] ↓ [Email with download link] (signed URL, 7 days TTL) , שמכניסה משימה לתור. עובד ה-DataExportService מייצר קבצי פרופיל (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()]);
    }
}

איך כתובות 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);
}

בחירת פורמט הייצוא הנכון

הפורמט תלוי במטרה: JSON ל-API ולחילוץ GDPR, CSV עם BOM לטבלאות ול-Excel, XLSX עם עיצוב לחשבונאות, XML לאינטגרציות B2B. אם יש צורך במספר קבצים, אנו משתמשים ב-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, משימות ניקוי תקופתיות.
  6. תיעוד — מסירת מסמכי ארכיטקטורה והוראות.

מה כלול

  • תיעוד ארכיטקטוני עם תיאורי זרימת נתונים.
  • קוד מקור עם הערות וטיפוסים קפדניים.
  • הוראות פריסה עם דוגמאות להגדרת תור ו-S3.
  • הכשרה לצוות תמיכה.
  • אחריות ל-3 חודשים לפעולה ללא הפרעות.

לוח זמנים ותמחור

ייצוא נתוני משתמש בסיסי (JSON + CSV ב-ZIP): 2–3 ימים. עם תור, S3 והתראת דוא"ל: 3–4 ימים. ייצוא תואם GDPR של כל הנתונים האישיים: 4–5 ימים. התמחור נקבע באופן אישי לפי נפחי נתונים ומספר הפורמטים. לצוות שלנו ניסיון של למעלה מ-10 שנים וביצע 40+ פרויקטי ייצוא נתונים. הזמינו ייצוא לפרויקט שלכם — קבלו ייעוץ חינמי והערכה תוך יום אחד.

צרו קשר כדי לדון בפרויקט שלכם ולקבל הערכה מדויקת.