Главная/Блог/Загрузка и хранение файлов в веб-приложении через S3: что учесть в разработке

Загрузка и хранение файлов в веб-приложении через S3: что учесть в разработке

Загрузка и хранение файлов в веб-приложении через S3: что учесть в разработке
Практически любое веб-приложение работает с файлами: клиенты загружают документы и фото, менеджеры прикрепляют договоры, маркетологи публикуют баннеры. На этапе прототипа файлы часто складывают прямо на диск сервера — и это работает, пока пользователей десятки. При росте нагрузки такой подход оборачивается потерянными файлами, тормозящей загрузкой и непредсказуемыми счетами за дисковое пространство. В статье разбираем, как правильно организовать загрузку и хранение файлов через S3-совместимое хранилище и какие вопросы обязательно поставить перед разработчиками.

Почему файлы не хранят на диске сервера и в базе данных

Локальный диск — самый простой и самый хрупкий вариант. Диск заполняется, бэкапы разрастаются, а при переезде на другой сервер или добавлении второй ноды файлы «остаются» на старой машине: приложение масштабируется, а хранилище — нет. Хранение файлов в базе данных в виде BLOB ещё хуже: база разбухает, резервные копии растягиваются на часы, а СУБД тратит ресурсы на то, для чего не предназначена. S3-хранилище снимает эти проблемы: оно масштабируется до любых объёмов, реплицирует данные автоматически и живёт отдельно от серверов приложения. У ведущих провайдеров заявленная надёжность хранения достигает «одиннадцати девяток» — 99,999999999% сохранности объектов.

Что такое S3 и какие хранилища доступны в России

S3 — это не конкретный сервис, а протокол (API), ставший отраслевым стандартом объектного хранения. Код, написанный под S3, работает с любым совместимым провайдером, поэтому при необходимости хранилище меняется без переписывания приложения. Основные варианты:
    <
  • Selectel — объектное хранилище с гибкими тарифами.
  • MinIO — open-source решение для развёртывания S3 на собственных серверах, если данные нельзя отдавать в облако.
Стоимость стандартного хранения — порядка 1,5–3 ₽ за ГБ в месяц (тарифы меняются, уточняйте актуальные цены). Для SDK на PHP, Python, Node.js и других языках базовая интеграция занимает один-два дня работы разработчика.

Схема загрузки: через бэкенд или напрямую в хранилище

Есть два пути. Первый — файл летит на ваш сервер, сервер проверяет его и перекладывает в хранилище. Просто в реализации, но сервер становится узким местом: он пропускает через себя весь трафик, а загрузка видео на 500 МБ занимает канал и ресурсы. Второй путь — прямая загрузка по временной подписанной ссылке (presigned URL): бэкенд выдаёт ссылку, браузер заливает файл напрямую в S3, а сервер получает только уведомление и сохраняет метаданные. Схема снимает нагрузку с приложения и позволяет принимать файлы любого размера — для объектов больше 100 МБ используется multipart-загрузка, когда файл режется на части и собирается уже на стороне хранилища. Подписанная ссылка действует ограниченное время (обычно 5–15 минут), поэтому использовать её «в обход» логики приложения нельзя.

Безопасность загрузки: минимум, который должен быть в любом проекте

Загруженный файл — классическая точка входа для атак, поэтому проверки обязательны независимо от бюджета проекта:
  • Белый список форматов. Проверять не только расширение, но и MIME-тип и «магические байты» — реальное содержимое файла.
  • Лимиты размера отдельно для каждого типа: например, 20 МБ для PDF-документов и 10 МБ для фото.
  • Антивирусная проверка (ClamAV или облачный сервис) до того, как файл станет доступен другим пользователям.
  • Переименование в UUID с сохранением оригинального имени в метаданных — защита от конфликтов и подмены путей.
  • Отдельный домен для выдачи файлов пользователей — чтобы вредоносный код в загруженном файле не исполнился в контексте вашего сайта.

Обработка медиа: миниатюры и современные форматы

Оригинал фото на 5 МБ не должен отдаваться в списке из 50 позиций — иначе страница будет грузиться десятки секунд. При загрузке приложение должно автоматически генерировать набор миниатюр под задачи интерфейса: например, превью 200×200 для списков, версию 800 px по ширине для карточек и сжатый оригинал. Конвертация в WebP или AVIF даёт ещё 25–35% экономии против JPEG при том же визуальном качестве. Для видео задайте разумные лимиты и заранее решите, нужно ли хранить тяжёлые исходники или достаточно сжатых версий. Продумайте и удаление: при удалении карточки товара или анкеты её файлы должны уходить из хранилища, иначе через год там скопятся гигабайты мусора.

Разграничение доступа: кто и как долго видит файл

По умолчанию бакет (контейнер хранилища) должен быть закрытым. Публичный доступ — только для того, что и должно быть общедоступным: баннеры, логотипы, иллюстрации статей. Документы пользователей — договоры, сканы паспортов, счета — выдаются по подписанным ссылкам с коротким сроком жизни: например, 15–60 минут. Так файл увидит только его владелец, даже если кто-то посторонний узнает адрес объекта. Важный нюанс: отозвать уже выданную ссылку раньше срока нельзя, поэтому TTL делают коротким, а критичные документы дополнительно защищают проверкой прав на сервере при каждом запросе новой ссылки. Для чувствительных данных настройте логирование обращений — кто, когда и какой файл скачивал.

Стоимость хранения и ускорение выдачи через CDN

Расходы на хранилище легко держать под контролем, если настроить две вещи. Первая — lifecycle-правила: автоматический перенос старых файлов в холодный класс хранения (он в 1,5–2 раза дешевле стандартного) и удаление временных объектов, например, через сутки. Вторая — мониторинг роста бакета с алертами, чтобы аномальный рост (например, из-за бага, дублирующего файлы) не превратился в счёт с лишними нулями. Скорость выдачи решает CDN — у большинства провайдеров он подключается за пару кликов. Статику вроде картинок интерфейса и баннеров кэшируйте агрессивно: заголовок кэша на год плюс имя файла с хэшем версии, чтобы обновления мгновенно доходили до пользователей.

Чек-лист вопросов к разработчикам перед стартом

Задайте эти вопросы на этапе проектирования — они покрывают 90% проблем с файлами в готовых продуктах:
  1. Где хранятся файлы: S3-хранилище или диск сервера — и почему выбран этот вариант?
  2. Загрузка идёт напрямую по presigned URL или через бэкенд?
  3. Какие проверки форматов, размеров и антивирус применяются к загружаемым файлам?
  4. Как устроено разграничение доступа: какие файлы публичные, какие приватные, какой срок жизни у ссылок?
  5. Генерируются ли миниатюры и форматы WebP/AVIF автоматически?
  6. Подключён ли CDN и настроены ли кэш-заголовки?
  7. Настроены ли версионирование, резервное копирование и lifecycle-правила?
  8. Что происходит с файлами при удалении пользователя, заказа или карточки?
Хранение файлов через S3 — стандарт не потому, что «так все делают», а потому что это решение одновременно закрывает масштабируемость, безопасность и предсказуемую стоимость. Диск сервера приемлем только для прототипа, который не жалко выбросить. Если в приложении загружаются документы, фото или видео — закладывайте архитектуру хранилища на этапе проектирования: presigned-загрузка, приватный бакет, валидация, миниатюры и lifecycle-правила. Переделывать файловую инфраструктуру на живом продукте с реальными данными в разы дороже, чем спроектировать её правильно сразу. Если сомневаетесь в архитектуре — покажите проект нам: разберём текущее решение и предложим вариант под ваш объём и бюджет.

Нужна разработка?

Оставьте заявку — обсудим ваш проект и предложим решение