Объектное хранение и S3: что, зачем и по какому адресу
Оглавление · Модели хранения · Garage
Задача: проследить путь байтов фотографии и отличить адрес объекта от его публичного URL. Нужны HTTP и различие файла и записи о файле.
Где хранить байты
| Вариант | Что удобно | Что нужно продумать |
|---|---|---|
| Файловая система хоста | Прямой доступ через файловый API | Права, пути, резервная копия и доступ других процессов |
| Docker volume | Сохранение данных вне слоя контейнера | Привязка к хосту, копирование и восстановление |
| База данных | Байты вместе с транзакционными данными | Объём, доставка файлов и нагрузка на БД |
| Объектное хранилище | Доступ к байтам через API по ключу | Доступ, метаданные, целостность связей и восстановление |
Отдельное хранилище полезно, когда файлы должны обслуживаться независимо от процесса приложения. Для небольшого локального инструмента может хватать каталога. Volume — способ сохранить данные контейнера, а не другой вид облачного API. Garage сам использует тома, чтобы сохранять объекты и служебные данные.
S3 — сервис и семейство совместимых API
Amazon S3 — объектное хранилище AWS. Bucket содержит объекты; объект имеет key, содержимое и метаданные. Ключ идентифицирует объект внутри bucket. Модель Amazon S3. Вводная модель здесь относится к обычному объектному bucket, не ко всем специальным типам bucket, появляющимся в AWS.
«S3-compatible» означает поддержку определённой части API. Это не обещание всех возможностей AWS, одинаковых прав, пределов или согласованности. Atmanki использует Garage 2.3.0; настройки и ограничения сверяем отдельно. Совместимость Garage.
Математическая модель без версионирования:
(bucket, key) → (bytes, metadata). Если версионирование включено и поддерживается,
идентичность чтения может включать version ID. Это не строка таблицы CMS и не
путь на диске операционной системы.
Bucket, key, prefix, endpoint и region
| Понятие | Учебный пример | Роль |
|---|---|---|
| Bucket | media | Контейнер объектов |
| Key | strapi/a.jpg | Имя конкретного объекта внутри bucket |
| Prefix | strapi/ | Начало ключа, по которому можно группировать/искать |
| API endpoint | http://s3:3900 | Адрес S3-протокола в Docker-сети |
| Region | garage | Параметр настройки клиента для этого хранилища |
| Public origin | http://media.localhost:8080 | Адрес чтения для браузера |
В обычной плоской модели / — часть key; префикс не создаёт каталог с POSIX-правами.
Папки интерфейса могут быть представлением ключей.
Ключи Amazon S3.
Папки медиабиблиотеки Strapi — ещё один уровень организации: они не обязаны
переносить объект или менять key.
Path-style обращение помещает bucket в путь: endpoint/media/strapi/a.jpg.
Virtual-hosted style использует имя bucket в host.
Адресация AWS. В конфигурации проекта
forcePathStyle=true; публичный website URL строится другим путём.
Не собирайте подпись S3 вручную по этой иллюстрации: SDK учитывает запрос и настройки.
Основные операции
| Операция | Что выясняем |
|---|---|
| PutObject | Сохранить объект по ключу |
| GetObject | Получить байты |
| HeadObject | Получить сведения без тела объекта |
| ListObjectsV2 | Перечислить объекты с учётом префикса и пагинации |
| DeleteObject | Удалить по правилам выбранного bucket |
Список метаданных не является скачиванием всех байтов. Одной страницы listing может быть недостаточно для аудита. ListObjectsV2. Удаление при версионировании может иметь иной смысл, чем физическое исчезновение всех версий. Удаление версий AWS. Для каждого продукта проверяйте поддерживаемый контракт, а не переносите ожидания из локальной папки.
У объекта есть Content-Type и другие метаданные. Заявленный MIME не доказывает, что внутри допустимое изображение. ETag — не универсальный MD5 файла: его смысл зависит от способа записи и настроек. Сведения об объекте AWS. Для проверки целостности заранее определите checksum и сравниваемые байты.
Multipart upload разбивает один объект на части: начало, загрузка частей, завершение; при отказе нужен разбор или отмена незавершённой загрузки. Это не HTML multipart/form-data, где форма передаёт поля и файлы HTTP-обработчику. Multipart S3. Порог и поведение SDK зависят от конфигурации; они не следуют из размера файла в нашем учебном примере.
Доступ и подписанные URL
Credentials разрешают операции клиента с хранилищем. Долговременный secret не передаётся браузеру. Публичное чтение — отдельная политика, а не отсутствие server-side credentials для загрузки.
Presigned URL разрешает определённый запрос от имени подписавшего клиента в пределах его прав и срока действия. URL можно использовать повторно, пока он действителен; это не одноразовый билет. Ограничения credentials также влияют на срок. Для upload уже существующий key может быть перезаписан по контракту. Подписанные URL AWS.
Исходник схемы
sequenceDiagram participant Browser as Браузер participant App as Приложение participant Store as Объектное хранилище Browser->>App: Запрос разрешения на загрузку App->>App: Проверить пользователя и параметры App-->>Browser: URL ограниченного запроса Browser->>Store: Загрузка по подписанному URL Store-->>Browser: Результат Browser->>App: Завершение загрузки для проверки и связи
Это будущий вариант интеграции, не действующий endpoint Atmanki. Приложение должно проверить завершение, key, формат и владение перед связыванием файла. Совместимость presigned-запросов с конкретной версией Garage проверяют отдельно. Не печатайте приватный подписанный URL в публичной документации.
Два пути в Atmanki
Исходник схемы
flowchart LR Editor["Редактор"] --> CMS["Strapi upload"] CMS -->|"S3 API: 3900"| API["Garage: bucket media"] CMS --> DB["CMS: запись файла и связи"] Browser["Браузер: /a.jpg"] --> Proxy["Caddy"] Proxy -->|"/strapi/a.jpg, website: 3902"| Website["Garage website endpoint"] Website --> API
Схема разделяет операции, не изображает единую транзакцию. Provider задаёт rootPath=strapi, S3_ENDPOINT, S3_PUBLIC_URL, forcePathStyle и ACL:undefined. Последнее важно для Garage, который не поддерживает AWS ACL; это не команда открыть доступ всем. Website-доступ bucket разрешён отдельно, как описано в инструкции.
CMS-адаптер проверяет origin и форму пути, выдаёт чистый public URL. Caddy добавляет strapi/ и выставляет Host для website endpoint. Старые /strapi/... остаются доступны; старые local uploads идут через /api/media/... . Внутренний API endpoint не становится адресом изображения для браузера.
CORS задаёт возможность браузерному скрипту читать cross-origin ответы; он не заменяет права хранилища. Обычное отображение изображения и доступ JS к его данным — разные сценарии. CORS S3. Кеширование публичного HTTP-ответа разобрано в главе о кешах.
Практика
Для вымышленного a.jpg выпишите отдельно bucket, key, API endpoint, website host и public URL. Найдите конфигурации, которые связывают их. Объясните, почему папка в Strapi, key и путь браузера могут различаться.
Продолжите в главе о жизненном цикле медиа: successful upload, запись CMS, публикация и восстановление требуют разных свидетельств.