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

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

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

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

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

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

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

Часті запитання

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

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1422
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1287
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    984
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1250
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    986
  • image_bitrix-bitrix-24-1c_fixper_448_0.webp
    Розробка веб-сайту для компанії ФІКСПЕР
    1000

Ви запустили 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 проєктів із завантаженням файлів.

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