Реалізація завантаження файлів: валідація, прогрес-бар, безпека

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

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

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

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

Послуги, які ми пропонуємо
Показано 1 з 1Усі 2062 послуг
Реалізація завантаження файлів: валідація, прогрес-бар, безпека
Простий
від 1 дня до 3 днів
Часті запитання

Ви запустили MVP за місяць, а на першому ж клієнтському файлі в 200 МБ сервер ліг — тайм-аут, 502, користувач у гніві. Знайомо? Проблема не в залізі, а у відсутності правильної реалізації завантаження файлів: без chunked upload, без валідації на клієнті та сервері, без прогрес-бара. Ми налаштовували такі кейси десятки разів, і щоразу стикалися з одними й тими ж граблями. У цій статті розповімо, як зробити upload правильно — з валідацією, прогресом, безпекою та підтримкою великих файлів.

Які проблеми вирішуємо?

Неочікуваний тайм-аут при завантаженні. Файл 200 МБ, Nginx налаштований на 30 секунд — сервер вбиває з'єднання, користувач перезавантажує сторінку. Рішення — chunked upload (розбиття на частини) або налаштування client_max_body_size та fastcgi_read_timeout, але це паліатив. Chunked upload працює завжди.

Валідація тільки на клієнті. Будь-який школяр відправить POST з curl і завантажить .exe замість .jpg. На сервері перевіряємо MIME через finfo, не довіряючи заголовку. Обмежуємо розмір, кількість файлів, перевіряємо сигнатуру.

Втрата прогресу. Користувач не бачить, скільки ще чекати, і закриває вкладку. Додаємо прогрес-бар через onUploadProgress (Axios) або XMLHttpRequest. Для chunked — показуємо частини. Економія на доробках та зниження кількості тікетів у підтримку — ось що дає правильне завантаження файлів.

Як ми це робимо: стек та реалізація

Використовуємо Laravel 11 (PHP 8.3) + S3 (MinIO або AWS) + React 18 (TypeScript). Для великих файлів — multipart upload через S3 SDK. Нижче — код, який працює в продакшені.

Сервер: Laravel

class FileUploadController extends Controller
{
    public function store(Request $request): JsonResponse
    {
        $request->validate([
            'file' => [
                'required',
                'file',
                'max:51200', // 50 MB в КБ
                'mimes:jpg,jpeg,png,gif,webp,pdf,docx,xlsx,zip',
            ],
        ]);

        $file = $request->file('file');

        // Генерируем безопасное имя — оригинальное имя не используем
        $filename = Str::uuid() . '.' . $file->getClientOriginalExtension();
        $path = 'uploads/' . auth()->id() . '/' . date('Y/m') . '/' . $filename;

        // Загрузка в S3
        Storage::disk('s3')->putFileAs(
            dirname($path),
            $file,
            basename($path),
            ['visibility' => 'private']
        );

        $upload = Upload::create([
            'user_id' => auth()->id(),
            'path' => $path,
            'original_name' => $file->getClientOriginalName(),
            'mime_type' => $file->getMimeType(),
            'size' => $file->getSize(),
        ]);

        return response()->json(['id' => $upload->id, 'path' => $path], 201);
    }
}

Клієнт: React з прогрес-баром

function FileUploader() {
  const [progress, setProgress] = useState(0);
  const [uploading, setUploading] = useState(false);

  async function handleUpload(e: React.ChangeEvent<HTMLInputElement>) {
    const file = e.target.files?.[0];
    if (!file) return;

    const formData = new FormData();
    formData.append('file', file);

    setUploading(true);
    try {
      await axios.post('/api/upload', formData, {
        headers: { 'Content-Type': 'multipart/form-data' },
        onUploadProgress: (e) => {
          setProgress(Math.round((e.loaded / (e.total ?? 1)) * 100));
        },
      });
    } finally {
      setUploading(false);
    }
  }

  return (
    <div>
      <input type="file" onChange={handleUpload} disabled={uploading} />
      {uploading && <progress value={progress} max={100}>{progress}%</progress>}
    </div>
  );
}

Chunked Upload для великих файлів

Файли >100 MB завантажують частинами через S3 Multipart Upload:

// Ініціалізація
public function initChunked(Request $request): JsonResponse
{
    $s3 = Storage::disk('s3')->getClient();
    $result = $s3->createMultipartUpload([
        'Bucket' => config('filesystems.disks.s3.bucket'),
        'Key' => 'uploads/' . Str::uuid() . '.' . $request->extension,
    ]);
    return response()->json(['upload_id' => $result['UploadId'], 'key' => $result['Key']]);
}

// Завантаження частини
public function uploadPart(Request $request): JsonResponse
{
    $s3 = Storage::disk('s3')->getClient();
    $result = $s3->uploadPart([
        'Bucket' => config('filesystems.disks.s3.bucket'),
        'Key' => $request->key,
        'UploadId' => $request->upload_id,
        'PartNumber' => $request->part_number,
        'Body' => $request->getContent(),
    ]);
    return response()->json(['etag' => $result['ETag']]);
}

Порівняння підходів: звичайний upload vs chunked

Параметр Звичайний upload Chunked upload
Тайм-аут Високий (>50 МБ) Низький (кожна частина маленька)
Прогрес Простий (один запит) Детальний (за частинами)
Відновлення Немає Так (з перерваної частини)
Складність Низька Середня (необхідний S3 SDK)
Краще для Файли < 50 МБ Файли > 50 МБ

Додатково: chunked upload знижує кількість тайм-аутів на 80% за нашими даними, що критично для користувацького досвіду. Вартість впровадження окупається за рахунок зниження навантаження на підтримку.

Чому варто обрати chunked upload?

Розберемо два підходи: звичайний upload vs chunked. Звичайний простіший у реалізації, але на файлах > 100 МБ дає велику кількість тайм-аутів (80% випадків за нашими даними). Chunked upload вирішує проблему, але потребує налаштування S3 та додаткових ендпоінтів. Ми використовуємо другий варіант для всіх проєктів, де планується завантаження великих файлів. Це виправдано: користувач не втрачає дані, не перезавантажує сторінку, а прогрес завантаження тримає його в курсі.

Як уникнути типових помилок?

Чек-лист: що перевірити перед деплоєм:

  • Забули про Nginx limit. client_max_body_size має бути більшим, ніж ваш max. Інакше 413.
  • Оригінальне ім'я файлу. Ніколи не зберігайте як є — використовуйте UUID.
  • Тільки одна перевірка. Валідація на клієнті + сервері обов'язкова.
  • Не налаштоване очищення. Якщо користувач почав завантаження, але не закінчив, файли висять у S3. Cron-задача раз на день видаляє "завислі" частини.

Етапи роботи та орієнтовні терміни

Етап Тривалість
Аналітика (типи файлів, розміри, локація) 1 день
Проєктування та вибір сховища (S3 vs локально) 0.5 дня
Реалізація контролерів, валідації, клієнтського коду 1–2 дні
Тестування (різні розміри, помилки, тайм-аути) 1 день
Деплой та налаштування S3, моніторингу 0.5 дня

Всього: 3–5 днів залежно від складності.

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

  • Документація по API ендпоінтам та форматам запитів.
  • Доступи до S3-сховища та дашборду для моніторингу.
  • Навчання команди роботі з новою функціональністю.
  • Підтримка після запуску — виправлення багів та оптимізація протягом місяця.

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

  1. Аналітика. Визначаємо типи файлів, максимальний розмір, локацію зберігання.
  2. Проєктування. Вирішуємо, чи потрібен chunked, де зберігати (S3/MinIO/локально).
  3. Реалізація. Пишемо контролери, валідацію, клієнтський код з прогрес-баром.
  4. Тестування. Завантажуємо файли різних розмірів, перевіряємо помилки, тайм-аути, безпеку.
  5. Деплой. Налаштовуємо S3, CI/CD, моніторинг.

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

Завантаження файлів з валідацією в S3 для Laravel/Node.js: 1–2 дні. Chunked upload + прогрес-бар: 2–3 дні. Вартість розраховується індивідуально — напишіть нам, і ми оцінимо ваш проєкт. Працюємо за договором з гарантією якості — 5+ років досвіду, понад 30 проєктів із завантаженням файлів.

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

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

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

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