До основного вмісту

S3 Object Storage: провайдери та інтеграція з PHP

Хмара та DevOps · 19.04.2026 · 3 хв читання
Ілюстрація до статті «S3 Object Storage: провайдери та інтеграція з PHP»

Хмарне сховище об'єктів: порівняння S3-провайдерів

Об'єктне сховище (Object Storage) — оптимальний спосіб зберігати статику, медіафайли, бекапи та ML-датасети. Усі провайдери використовують сумісний із S3 API, що спрощує зміну провайдера.

Порівняння S3-провайдерів

ПровайдерЦіна (GB/міс)Вихідний трафікРегіон
Hetzner Object Storage€0.0049€0.010/GB (перші 1TB безкоштовно)Німеччина, Фінляндія
Backblaze B2$0.006$0.01/GBСША, ЄС
Cloudflare R2$0.015БЕЗКОШТОВНОГлобально
DigitalOcean Spaces$0.020$0.01/GB (перші 1TB безкоштовно)США, ЄС, Азія
AWS S3$0.023$0.09/GBГлобально

Cloudflare R2: безкоштовний вихідний трафік

# R2 через rclone
[cloudflare-r2]
type = s3
provider = Cloudflare
access_key_id = YOUR_R2_ACCESS_KEY
secret_access_key = YOUR_R2_SECRET_KEY
endpoint = https://ACCOUNT_ID.r2.cloudflarestorage.com
region = auto

Підключення до WordPress (WP Offload Media)

# wp-config.php
define('AS3CF_SETTINGS', serialize([
    'provider'    => 's3',
    'access-key-id'     => 'YOUR_KEY',
    'secret-access-key' => 'YOUR_SECRET',
    'bucket'      => 'my-wp-media',
    'region'      => 'eu-central-1',
    'domain'      => 'path',  // або власний домен через CDN
]));

PHP: завантаження файлів у S3

use Aws\S3\S3Client;

$s3 = new S3Client([
    'version'     => 'latest',
    'region'      => 'eu-central-1',
    'endpoint'    => 'https://YOUR_ENDPOINT',
    'credentials' => ['key' => 'KEY', 'secret' => 'SECRET'],
    'use_path_style_endpoint' => true,
]);

// Завантажити файл
$s3->putObject([
    'Bucket'      => 'my-bucket',
    'Key'         => 'uploads/' . basename($_FILES['file']['name']),
    'SourceFile'  => $_FILES['file']['tmp_name'],
    'ACL'         => 'public-read',
    'ContentType' => $_FILES['file']['type'],
]);

// Отримати публічний URL
$url = $s3->getObjectUrl('my-bucket', 'uploads/photo.jpg');
Рекомендація: Для європейських клієнтів використовуйте Hetzner Object Storage (найдешевший варіант для зберігання) або Cloudflare R2 (якщо важливий вихідний трафік). Обидва сумісні з AWS S3 SDK.

Розбір реальних помилок S3 API

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

SignatureDoesNotMatch

<Error><Code>SignatureDoesNotMatch</Code><Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message></Error>

Класичні причини: неправильний secret key (зайвий пробіл під час копіювання), розсинхронізація системного часу сервера більш ніж на 15 хвилин, або не виставлено use_path_style_endpoint => true для провайдерів, які не підтримують virtual-hosted style (MinIO, Hetzner). Перевірте час командою timedatectl і синхронізуйте через chronyd або ntpd.

AccessControlListNotSupported

<Error><Code>AccessControlListNotSupported</Code><Message>The bucket does not allow ACLs</Message></Error>

З квітня 2023 року нові бакети AWS S3 створюються з Object Ownership Bucket owner enforced, який повністю вимикає ACL. Код із 'ACL' => 'public-read' у putObject починає падати з цією помилкою. Рішення — прибрати параметр ACL і відкрити доступ через bucket policy у JSON замість ACL на рівні об'єкта.

Коли об'єктне сховище не підходить

  • Потрібна POSIX-семантика з блокуваннями файлів і атомарним дозаписом — S3 це не файлова система, а сховище незмінних об'єктів цілком.
  • Застосунку потрібна низька затримка для блокового доступу бази даних — використовуйте локальний NVMe або мережевий блоковий диск, а не об'єктне API.
  • Зберігається величезна кількість дуже дрібних файлів із високою частотою запитів — білінг за операції (PUT/GET) починає перевищувати білінг за обсяг, розумніше об'єднувати файли або ставити CDN-кеш перед бакетом.
  • Потрібен запис у реальному часі з мінімальною затримкою (потокове відео, телеметрія) — об'єктне сховище проєктувалося для пакетного запису, а не для потоку.
  • Регуляторні вимоги зобов'язують до фізичного контролю над носієм — тоді розгортайте власний MinIO на своєму залізі замість хмарного провайдера.

Перевірка результату

# Завантажити тестовий файл і звірити контрольну суму
aws s3 cp test.txt s3://my-bucket/test.txt --endpoint-url https://YOUR_ENDPOINT
aws s3api head-object --bucket my-bucket --key test.txt --endpoint-url https://YOUR_ENDPOINT

# Перевірити публічний доступ, якщо бакет має бути відкритим
curl -I https://my-bucket.YOUR_ENDPOINT/test.txt

# Порівняти локальний і віддалений MD5
md5sum test.txt
aws s3api head-object --bucket my-bucket --key test.txt --query ETag --endpoint-url https://YOUR_ENDPOINT

Код відповіді 403 на публічний curl -I майже завжди означає, що bucket policy не дозволяє анонімне читання, а не проблему з ключами доступу — ключі взагалі не потрібні для читання публічного об'єкта. Якщо об'єктне сховище використовується для резервних копій, логічніше ганяти їх через rclone, а не писати код на PHP під разову задачу.

Чи була стаття корисною?
← Назад до бази знань Поставити питання підтримці