Технологии

S3-хранилище: где хранить файлы приложения, а не в базе данных

25 минАктуально на 12 августа 2026

S3-хранилище для файлов приложения

Ты добавил в своё приложение загрузку аватарок: пользователь выбирает картинку, жмёт кнопку — и она появляется в профиле. Всё работает, пока файлов десять и живут они локально. А потом ты перезаливаешь приложение на сервер — и все аватарки исчезают. Или база данных вдруг весит три гигабайта, резервная копия делается полчаса, а счёт за хостинг растёт. Знакомая ситуация у каждого, кто впервые делает приложение с пользовательскими файлами. Решение у неё одно, и у него есть имя — S3-хранилище. Ниже — простыми словами о том, что это такое, почему файлам не место ни в базе данных, ни на диске сервера, как устроены ключевые понятия вроде bucket и presigned URL и какой сервис выбрать новичку из России.

Содержание
  1. Что такое S3-хранилище простыми словами
  2. Почему файлы нельзя хранить в базе данных
  3. Почему файлы нельзя хранить на диске своего сервера
  4. Ключевые понятия: bucket, object key и presigned URL
  5. Какое объектное хранилище выбрать: сравнение сервисов
  6. Как подключить S3-хранилище к своему приложению
  7. Безопасность: куда девать ключи доступа
  8. Когда S3-хранилище действительно нужно
  9. Частые вопросы
  10. Заключение

Что такое S3-хранилище простыми словами

S3-хранилище — это специальный сервис, который хранит файлы: картинки, видео, документы, архивы — отдельно от базы данных и отдельно от сервера, где крутится твоё приложение. Твоё приложение обращается к нему по сети: «положи вот этот файл», «дай вот этот файл», «удали вот этот». Всё, больше хранилище ничего не умеет — и в этом его сила.

Правильное техническое название — объектное хранилище. Файл там называется «объектом», потому что хранится целиком, как неделимая вещь, вместе со своим именем и служебной информацией: тип файла, размер, дата загрузки. Это принципиально отличается от базы данных, где информация разложена по таблицам, строкам и колонкам, и от обычной файловой системы, где файлы разбросаны по дереву папок на конкретном диске конкретного компьютера. Объектному хранилищу всё равно, на скольких физических машинах лежит твой файл: снаружи это единый сервис с единым адресом.

Откуда взялась буква и цифра в названии? S3 — это сокращение от Simple Storage Service, первого массового объектного хранилища, которое Amazon запустила ещё в 2006 году. Сервис стал настолько популярным, что его способ общения с приложениями — S3 API — превратился в фактический стандарт. Сегодня десятки провайдеров предлагают хранилища, которые «понимают» тот же самый S3-совместимый API. Для тебя как для разработчика это отличная новость: код, который работает с одним S3-хранилищем, почти без изменений заработает с любым другим. Поменял адрес сервера и ключи доступа — и переехал к другому провайдеру, не переписывая приложение.

Полезная аналогия. База данных — это картотека в офисе: карточки с записями, связями, ссылками друг на друга. А S3-хранилище — это склад на окраине с пронумерованными ячейками. В картотеке ты не хранишь сами вещи — ты хранишь карточку «ящик №42, третий ряд». Так и в приложении: в базе лежит запись «у пользователя Ивана аватарка лежит по такому-то адресу в хранилище», а сам файл — на «складе». Картотека лёгкая, поиск по ней быстрый, а склад справляется с любыми коробками любого размера.

Ещё одно важное свойство, о котором редко думают в начале: надёжность. Объектные хранилища по умолчанию дублируют каждый файл на несколько машин внутри дата-центра, а иногда и между дата-центрами. Провайдеры заявляют очень высокую долговечность данных — то есть вероятность потерять файл из-за сбоя оборудования ничтожно мала. Добиться такого же уровня надёжности на одном арендованном сервере своими руками — отдельный инженерный проект.

И последний штрих к картине: у объектного хранилища нет привычных операций «открыть файл и дописать кусок в середину». Объект либо загружается целиком, либо заменяется целиком, либо скачивается. Для аватарок, видео и документов это не ограничение, а норма — такие файлы и не редактируют по кусочкам. Зато именно благодаря этой простоте хранилище и умеет то, чего не умеют базы и диски: хранить миллионы файлов, раздавать их тысячам пользователей одновременно и стоить при этом недорого.

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

Первое, что приходит в голову новичку: «у меня уже есть база данных, положу файлы туда». Технически это возможно — у большинства баз есть тип данных для бинарных объектов. Но на практике это одна из самых дорогих ошибок. В статье про базы данных для новичка мы разбирали, что база отлично справляется с текстом, числами, датами и связями между записями — это её стихия. А вот с большими бинарными файлами она справляется плохо, и вот почему.

Резервные копии раздуваются. Базу данных принято регулярно копировать — это твоя страховка на случай сбоя. Если в базе лежит только текст, копия весит мегабайты и делается за секунды. Если туда же напихали гигабайты картинок и видео, каждая копия превращается в многогигабайтный архив, который долго создаётся, долго скачивается и занимает дорогое место. При этом сами файлы не меняются — копировать их заново каждый день бессмысленно, но база будет делать это снова и снова, потому что не умеет отличить «вечную» картинку от свежей текстовой записи.

Запросы замедляются. База данных устроена так, чтобы быстро находить маленькие записи по условиям: «дай всех пользователей, зарегистрированных вчера». Когда таблица забита тяжёлыми бинарными полями, внутренние механизмы кэширования и индексации начинают работать хуже. База тратит память и процессор на то, для чего не предназначена, и это сказывается даже на тех запросах, которые к файлам отношения не имеют.

Растёт счёт за хостинг. Место, где живёт база данных, — самое дорогое хранилище из всех: быстрые диски, резервирование, реплики. Объектное хранилище за тот же гигабайт берёт заметно меньше, потому что оно спроектировано именно под файлы и использует более дешёвую инфраструктуру. Платить за гигабайт в базе вместо гигабайта в S3 — всё равно что хранить коробки с вещами в арендованном офисе вместо склада.

Отдавать файлы неудобно. Когда браузер пользователя просит картинку, приложению приходится вытащить её из базы, а потом передать по сети. Каждый просмотр картинки — это нагрузка и на базу, и на приложение: сотня человек, открывших ленту с фотографиями, заставит твою базу работать файловым сервером. С S3 всё проще: приложение отдаёт браузеру ссылку, и браузер скачивает файл напрямую из хранилища, минуя твой сервер вообще. Хранилище построено именно для раздачи и переварит такой трафик играючи.

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

Вторая идея новичка: «сохраню файлы в папку uploads рядом с кодом приложения». Так делают все учебные примеры, и локально это действительно работает. Проблемы начинаются при публикации — и их сразу три.

Диск сервера часто недолговечен. Современные платформы развёртывания при каждом обновлении приложения собирают его заново, в чистом окружении. Всё, что приложение записало на диск в прошлый раз, при переразвёртывании просто стирается. Мы подробно разбирали этот момент в статье про деплой на Amvera: загрузил новую версию кода — и папка uploads со всеми пользовательскими файлами исчезла. Представь лицо пользователя, который зашёл в приложение, а его аватарка и все документы пропали после твоего «безобидного» обновления.

Один сервер не масштабируется. Пока у тебя сто пользователей и сотня картинок, раздача файлов с того же сервера не проблема. Но если приложение выстрелит и картинки начнут смотреть тысячи людей одновременно, весь трафик упрётся в один сервер: канал, диск, процессор. Объектные хранилища изначально построены для раздачи файлов в любых объёмах — это их основная работа, и растущая популярность приложения для них не стресс, а норма.

Файл «привязан» к одной машине. Как только приложение разрастётся до нескольких серверов за балансировщиком нагрузки, локальный диск сломается концептуально: пользователь загрузил файл на сервер А, а следующий его запрос попал на сервер Б — и там файла нет. Чтобы это починить, всё равно придётся выносить файлы в общее внешнее хранилище. Проще сделать это сразу, чем переписывать под нагрузкой.

Нет резервирования. Диск один — значит, единственная копия файлов одна. Сбой диска, ошибка в скрипте, случайное удаление — и файлы потеряны навсегда. Хранилища, как мы уже говорили, по умолчанию дублируют каждый объект на несколько машин.

Общий принцип звучит так: сервер приложения должен быть «безгосударственным» — его можно в любой момент удалить и поднять заново из кода, ничего не потеряв. Всё, что накапливается со временем, живёт вовне: данные — в базе, файлы — в объектном хранилище.

Ключевые понятия: bucket, object key и presigned URL

Чтобы не плавать в документации, достаточно выучить четыре термина. Они одинаковые у всех S3-совместимых сервисов — и у AWS, и у Яндекса, и у MinIO.

Bucket (бакет) — контейнер для файлов, аналог папки верхнего уровня. Обычно под приложение создают один бакет, например my-snake-app, а внутри раскладывают файлы по «подпапкам»: аватарки отдельно, скриншоты отдельно. Имя бакета уникально в рамках всего сервиса, как доменное имя, поэтому короткие красивые имена часто заняты. У бакета есть настройки: регион, где физически лежат данные, права доступа, правила автоматического удаления старых файлов.

Object key (ключ объекта) — полное имя-путь файла внутри бакета, например avatars/ivan/photo.png. Формально в объектном хранилище нет настоящих папок: есть плоский список объектов, а «папки» — просто префиксы в именах. Но веб-интерфейсы и библиотеки показывают это как привычное дерево, так что разницы в использовании ты не заметишь. Пара «бакет + ключ» однозначно указывает на файл — это его адрес внутри хранилища.

Публичный и приватный доступ. Бакет или отдельный объект можно сделать публичным — тогда файл доступен любому по прямой ссылке. Так делают для картинок сайта, логотипов, открытых материалов. Приватный объект по прямой ссылке не откроется: хранилище потребует подтверждения прав. Так хранят личные документы пользователей, скриншоты, служебные выгрузки. По умолчанию всё приватное — и это правильно: утечки данных через случайно открытые бакеты регулярно попадают в новости, причём страдают даже крупные компании.

Presigned URL (предподписанная ссылка) — самый элегантный механизм во всей этой системе. Проблема: как дать браузеру пользователя загрузить или скачать приватный файл, не сообщая ему секретные ключи от хранилища? Ответ: приложение на своём сервере, где ключи в безопасности, генерирует специальную временную ссылку — с криптографической подписью, которая действует ограниченное время (ты сам задаёшь срок, например 15 минут) и разрешает одну конкретную операцию с одним конкретным файлом. Браузер получает эту ссылку и работает с файлом напрямую. Ключи никуда не утекают, файл не гоняется через твой сервер, а по истечении срока ссылка становится бесполезной.

К этим четырём терминам полезно добавить ещё два слова, которые встретятся в настройках библиотеки. Endpoint — это адрес сервера хранилища, к которому обращается приложение; именно сменой endpoint и отличается подключение к разным провайдерам. Регион — это дата-центр (или группа дата-центров), где физически лежат твои файлы; регион выбирают поближе к пользователям, чтобы сократить задержки.

Вот как выглядит типичный цикл загрузки файла целиком:

flowchart TB
    U["Пользователь выбирает файл"] --> A["Приложение генерирует presigned URL"]
    A --> S["Файл загружается напрямую в S3-хранилище"]
    S --> D["В базу данных записывается только ссылка"]
    D --> P["При показе браузер забирает файл прямо из хранилища"]

Обрати внимание, чего на этой схеме нет: файл ни разу не проходит через базу данных. База хранит только текстовую строку-ссылку — а это её любимая работа. И обрати внимание, насколько разгружен твой сервер: он участвует только в выдаче коротких ссылок, а тяжёлые байты ходят между браузером и хранилищем напрямую.

Какое объектное хранилище выбрать: сравнение сервисов

Раз API везде одинаковый, выбор сводится к практическим вопросам: как платить, нужен ли VPN, где физически стоят серверы и сколько стоит трафик. Вот честная сравнительная таблица вариантов, о которых чаще всего спрашивают новички из России:

Сервис S3-совместимость Оплата из РФ Доступ без VPN Кому подходит
AWS S3 Оригинал, эталон Нужна иностранная карта Может требоваться VPN Те, у кого уже есть зарубежная оплата и опыт
Cloudflare R2 Да Нужна иностранная карта Может требоваться VPN Проекты с большим трафиком раздачи: нет платы за исходящий трафик
Yandex Object Storage Да Да, рубли, счёт для самозанятых и ИП Да Основной вариант для большинства читателей курса
Selectel Объектное хранилище Да Да, рубли Да Альтернатива Яндексу, тоже российский провайдер
MinIO (свой сервер) Да, это сама суть проекта Бесплатно (платишь только за свой сервер) Да Эксперименты, локальная разработка, полный контроль

Пара пояснений к таблице.

AWS S3 — это оригинал, с которого всё началось, и эталон совместимости: если что-то работает на AWS S3, оно почти наверняка заработает и у остальных. Но для разработчика из России путь сюда лежит через иностранную карту и обход ограничений — ради учебного проекта это лишние сложности.

Cloudflare R2 часто вспоминают в обсуждениях из-за необычной тарифной модели: сервис не берёт плату за исходящий трафик, то есть за раздачу файлов пользователям. У классических хранилищ трафик — отдельная и часто главная строка расходов, поэтому для приложения, которое отдаёт много картинок и видео, R2 даёт заметную экономию. Но оплата и доступ из России — с теми же зарубежными оговорками, что и у AWS.

Yandex Object Storage и Selectel — главный практический вывод таблицы. Регистрация с российским номером, оплата в рублях, закрывающие документы для самозанятых и ИП, никаких VPN и иностранных карт. Серверы в России — файлы быстро отдаются российским пользователям. Для первого приложения с аватарками и скриншотами этого более чем достаточно. Актуальные тарифы смотри на сайтах провайдеров: цены и условия меняются, а плата обычно складывается из трёх составляющих — объёма хранения, количества операций (загрузок и скачиваний) и исходящего трафика.

MinIO заслуживает отдельного абзаца. Это не облачный сервис, а программа с открытым кодом: поднимаешь на своём сервере — получаешь собственное S3-хранилище. Именно поэтому запрос «s3 хранилище minio» так популярен: люди ищут способ получить S3 без облака. MinIO удобен для локальной разработки — код пишешь и тестируешь дома против своего MinIO, а в боевом окружении переключаешься на облачный сервис, меняя только настройки. Для учебного проекта вроде «Змейки» это отличный способ потрогать S3 API бесплатно и без регистраций. Минус очевиден: своё хранилище — это свои заботы о дисках, бэкапах и доступности. Если твой сервер умрёт, спасать файлы придётся самому.

Как подключить S3-хранилище к своему приложению

Хорошая новость: тебе не нужно реализовывать протокол S3 руками. Схема подключения одинакова для всех сервисов и сводится к четырём шагам.

Шаг 1. Создай бакет. Регистрируешься у провайдера, в личном кабинете создаёшь bucket с понятным именем и оставляешь его приватным, если файлы не предназначены для всех. Выбирай регион поближе к своим пользователям — так файлы будут отдаваться быстрее.

Шаг 2. Получи ключи доступа. В том же личном кабинете генерируешь пару: access key (публичный идентификатор, как логин) и secret key (секретный ключ, как пароль). Этой парой будет подписывать запросы только твой сервер. Secret key показывают один раз — сразу сохрани его в надёжное место.

Шаг 3. Подключи библиотеку. Для JavaScript-проектов есть готовый npm-пакет — официальный aws-sdk (точнее, его модуль для S3) или совместимые альтернативы. Несмотря на название, библиотека работает с любым S3-совместимым сервисом: указываешь адрес хранилища (endpoint), регион, ключи — и дальше вызываешь готовые методы: загрузить файл, получить ссылку, удалить объект. Если пишешь приложение вместе с ИИ-ассистентом, задача формулируется буквально так: «подключи S3-совместимое хранилище, загружай аватарку пользователя через presigned URL и сохраняй ссылку на неё в базе». Ассистент подставит конкретный синтаксис под твой стек.

Шаг 4. В базу — только ссылку. После успешной загрузки хранилище подтверждает, что объект на месте. Приложение склеивает из бакета и ключа адрес файла и записывает эту текстовую строку в базу данных — в обычное текстовое поле рядом с пользователем. Сам файл в базе не появляется никогда. Когда нужно показать аватарку, приложение читает строку из базы и подставляет её в тег картинки — браузер скачивает файл напрямую из хранилища.

Один технический нюанс, о котором новички узнают из сообщений об ошибках: если браузер загружает файл напрямую в хранилище по presigned URL, в настройках бакета нужно разрешить запросы с домена твоего приложения — это называется CORS. Без этого разрешения браузер заблокирует загрузку из соображений безопасности. Настраивается один раз в личном кабинете провайдера.

Чтобы картина сложилась окончательно, вот весь серверный код загрузки аватарки в виде схемы — без привязки к конкретной библиотеке и синтаксису:

1. Браузер: «хочу загрузить файл ivan.png, 2 МБ»
2. Сервер проверяет: пользователь вошёл, формат — картинка, размер в лимите
3. Сервер генерирует presigned URL на загрузку в бакет, ключ "avatars/ivan.png"
4. Браузер получает ссылку и отправляет файл прямо в хранилище
5. Хранилище подтверждает загрузку
6. Сервер записывает в базу: user ivan → avatar = "avatars/ivan.png"

Шесть строк логики — и ни одного файла через сервер. Когда дойдёшь до реализации, ИИ-ассистент напишет из этой схемы рабочий код под твой проект: твоя задача — понимать шаги и следить, чтобы ключи оставались на сервере.

Типичные ошибки на этом этапе: попытаться проксировать все файлы через свой сервер «потому что так проще» (теряется весь смысл хранилища — сервер снова становится узким местом), сделать бакет публичным «чтобы точно открывалось» (личные файлы пользователей увидит кто угодно) и забыть ограничить размер и тип загружаемых файлов (пользователь зальёт десятикигабайтное видео вместо аватарки — а платить за хранение будешь ты). Проверяй формат и размер до генерации presigned URL, а лучше и после загрузки тоже.

Безопасность: куда девать ключи доступа

Access key и secret key от хранилища — это полномочия. Тот, кто ими завладел, может читать и удалять твои файлы, а главное — заливать свои, за хранение которых заплатишь ты. Известны случаи, когда слитые ключи использовали для размещения чужого контента, а владельцу приходил счёт на круглую сумму.

Правила простые и не обсуждаются:

  • ключи никогда не попадают в код и не коммитятся в git — даже в «временный» коммит, даже в приватный репозиторий;
  • ключи лежат в файле .env, который добавлен в .gitignore, а на боевом сервере — в настройках переменных окружения платформы;
  • в браузерный (клиентский) код ключи не вшиваются ни в каком виде — всё, что попало в браузер, видно любому пользователю через «просмотр кода»; именно для этого и существуют presigned URL;
  • если ключ всё-таки засветился — в коммите, скриншоте, переписке — немедленно отзови его в личном кабинете и сгенерируй новый; боты сканируют публичные репозитории на ключи постоянно, и счёт идёт на минуты.

Подробно про то, как правильно хранить пароли, токены и ключи, мы разбирали в статье про хранение секретов и токенов — если ещё не читал, самое время: ошибки с секретами самые дорогие из всех ошибок новичка.

Дополнительно: заведи для приложения отдельный ключ с минимальными правами — только запись и чтение в один конкретный бакет, без права удалять бакеты и управлять аккаунтом. Если такой ключ и утечёт, ущерб будет ограничен одним бакетом.

Когда S3-хранилище действительно нужно

Честный вывод: не каждому приложению нужно объектное хранилище, и подключать его «на вырост» — такая же ошибка, как класть файлы в базу.

S3-хранилище оправдано, когда в приложении появляются файлы, которые загружают сами пользователи: аватарки, скриншоты, фото, документы, игровые ассеты, аудио. Особенно когда таких файлов много и каждый весит больше нескольких мегабайт. Здесь без хранилища никак: база захлебнётся, а диск сервера не переживёт первого же обновления.

Есть и менее очевидный случай: файлы, которые создаёт само приложение. Если твоя «Змейка» генерирует пользователю красивую карточку с рекордом для соцсетей или сохраняет скриншоты лучших игр, это тоже накапливающиеся файлы, а не часть кода — им тоже место в хранилище, а не в папке проекта. Тот же принцип касается бэкапов, экспортов данных и любых отчётов, которые приложение собирает «на лету».

И наоборот: если у твоей «Змейки» есть пяток иконок интерфейса, логотип и пара фоновых картинок — ничего никуда выносить не надо. Эти файлы часть самого приложения, они меняются только вместе с кодом, и им самое место в публичной папке проекта, рядом с остальными файлами. Они попадут на сервер при обычном развёртывании и никуда не денутся.

Граница проходит по вопросу: «кто создаёт эти файлы?» Разработчик вместе с кодом — папка проекта. Пользователи в процессе работы — объектное хранилище плюс ссылка в базе.

Частые вопросы

Чем S3-хранилище отличается от обычного облачного диска вроде Google Drive?

Облачный диск сделан для человека: папки, общий доступ через интерфейс, синхронизация с компьютером. S3-хранилище сделано для программ: у него нет «интерфейса для людей», есть API, через который приложение кладёт и забирает файлы. Плюс тарификация под машинные нагрузки: гигабайты хранения и трафик раздачи, а не «место в аккаунте». Встроить Google Drive как файловое хранилище приложения — значит влезть в его ограничения и условия использования, которые для этого не предназначены.

Обязательно ли пользоваться именно Amazon S3?

Нет. Amazon S3 — лишь первый и самый известный сервис. Сегодня S3-совместимый API предлагают десятки провайдеров, включая российские Yandex Object Storage и Selectel, которые работают без VPN и принимают оплату в рублях. Код приложения от смены провайдера почти не меняется — обычно достаточно поменять адрес сервера и ключи.

Что такое presigned URL и зачем он нужен?

Это временная подписанная ссылка на один конкретный файл и одну операцию — загрузку или скачивание. Приложение генерирует её на сервере, где хранятся секретные ключи, и отдаёт браузеру пользователя. Браузер работает с файлом напрямую, ключи никому не показываются, а через заданное время ссылка перестаёт действовать. Это стандартный способ организовать загрузку и раздачу приватных файлов.

Можно ли поднять своё S3-хранилище бесплатно?

Да — для этого существует MinIO, программа с открытым кодом, реализующая S3 API. Её ставят на свой сервер или даже на домашний компьютер. Это популярный вариант для локальной разработки и экспериментов: код пишется против своего MinIO, а потом без переделок переключается на облачный сервис. Подробности — на официальном сайте проекта: min.io.

Что произойдёт, если я случайно закоммичу ключи в git?

Считай ключи скомпрометированными, даже если удалишь их следующим коммитом: история git сохраняет всё, а боты сканируют публичные репозитории на ключи постоянно. Правильное действие — немедленно отозвать ключ в личном кабинете провайдера, сгенерировать новый и прописать его в .env. И на будущее — проверить, что .env есть в .gitignore.

Сколько стоит S3-хранилище для небольшого приложения?

Плата складывается из объёма хранения, количества операций и исходящего трафика. Для учебного приложения с сотнями пользователей суммы обычно символические, а у некоторых провайдеров есть бесплатные лимиты на старт. Точные цифры меняются — актуальные тарифы смотри на сайте выбранного сервиса, там же обычно есть калькулятор стоимости.

Заключение

Файлы и база данных — разные виды нагрузки, и смешивать их не нужно. Запомни цепочку: сам файл живёт в S3-совместимом объектном хранилище, в базе данных лежит только текстовая ссылка на него, а сервер приложения остаётся «безгосударственным» — его можно пересоздать в любой момент, ничего не потеряв. Для новичка из России практичнее всего начать с российского S3-совместимого сервиса — регистрация простая, оплата в рублях, VPN не нужен, а API тот же самый, что у мирового эталона. А когда пользователи твоей «Змейки» начнут ставить себе аватарки, ты уже будешь знать, где этим аватаркам самое место.

И последнее. Не откладывай переезд файлов «на потом, когда вырастем»: архитектурные долги такого рода дорожают с каждым загруженным файлом, потому что потом придётся переносить всё накопленное под нажимом жалоб пользователей. Если в приложении уже есть или скоро появятся пользовательские файлы — заложи хранилище сразу. Это один из немногих случаев, когда «правильно с самого начала» стоит столько же, сколько «как-нибудь потом переделать».

Читай дальше

Все статьи

Не просто статьи — тебя доведут до результата

В практикуме за 2499 ₽ рядом живая команда практикующих разработчиков и маркетологов: ведём по шагам до твоего работающего приложения. Не «ролики и сам разбирайся» — помогаем на каждом затыке.

Перейти к практикуму
Все статьи Ещё: технологии и архитектура