Модель работы
Из одной идеи следует почти всё остальное: клиент SDK — это ключ к одному бакету. Он не хранит файлов и не знает про другие бакеты; он превращает вызовы методов в запросы к серверу, а ответы — в неизменяемые объекты.
Один клиент — один ключ — один бакет
В S5W бакет определяется ключом: в адресах API бакета нет вообще. Сервер смотрит на ключ и сам понимает, к какому бакету относится запрос. SDK повторяет эту модель:
S5w (клиент) ── S5wConfig ── адрес сервера + ключ ──► один бакет
│
папки, файлы, загрузки этого бакетаОтсюда три следствия:
- Бакет нигде не указывается. Ни в конфигурации, ни в методах. Ошибиться бакетом невозможно — в протоколе его просто нет.
- Несколько бакетов — несколько клиентов. Каждому свой ключ и свой объект
S5w. В Winter это несколько именованных бинов, см. Интеграция с Winter. - Права определяет ключ. Ключ BASIC годится только для
check(); для папок, файлов и загрузок нужен FULL. SDK не проверяет это заранее — сервер ответит 403, и вы получитеForbiddenException.
Клиент и ресурсы
Клиент — точка входа. Сами операции сгруппированы по ресурсам API, как и на сервере:
| Вызов | Что внутри | Адрес API |
|---|---|---|
$s5w->check() |
проверка ключа, место в бакете | GET /v1/check |
$s5w->folders() |
папки | /v1/folders |
$s5w->files() |
файлы: список, чтение, загрузка, изменение, удаление | /v1/files |
$s5w->uploads() |
загрузка частями | /v1/uploads |
Ресурс создаётся при первом обращении и дальше переиспользуется — $s5w->files() можно
вызывать сколько угодно раз, это ничего не стоит. Ни клиент, ни ресурсы не хранят состояния
между вызовами, поэтому один экземпляр безопасно использовать во всём приложении.
Ответы — это снимки, а не живые объекты
Каждый метод возвращает объект, собранный из ответа сервера: File, Folder, Page,
UploadSession. Все они неизменяемы (readonly) и описывают состояние на момент ответа.
$file = $s5w->files()->get($id);
$s5w->files()->rename($file, 'new.txt');
$file->name; // по-прежнему старое имя — $file это снимок до переименованияЧтобы получить новое состояние, возьмите то, что вернул метод изменения, или перечитайте объект:
$file = $s5w->files()->rename($file, 'new.txt'); // вернул новый снимок
$file = $s5w->files()->get($file); // или перечитатьЭто же правило работает в загрузке частями: каждый append() возвращает новую
UploadSession с новым смещением, а старую можно выбросить.
Объект вместо id
Методы, которые принимают id файла, принимают и сам объект File; параметр folder:
принимает и имя папки, и объект Folder. Поэтому результат одного вызова можно сразу
передавать в следующий.
Файл адресуется по id
У каждого файла есть id — 16 символов из A-Za-z0-9_- (на сервере это называется slug).
Именно по нему файл находят методы SDK и адреса раздачи /p/{id} и /o/{bucket}/{id}.
- id не меняется при переименовании и переносе — выданные ссылки продолжают работать;
- id безопасно хранить у себя в базе, писать в логи и показывать в URL — внутренние номера сервер наружу не отдаёт;
- имя — не идентификатор. Имя уникально только внутри папки, и при совпадении сервер
сам добавит суффикс:
report.pdf→report (1).pdf.
Строку, которая не может быть id, SDK отклоняет сам, без запроса: find('abc') вернёт
null, а get('abc') бросит NotFoundException.
Что происходит при вызове
Возьмём $s5w->files()->upload('/tmp/video.mp4', folder: 'videos'):
files()->upload()
│
├─ файл ≤ 16 МиБ ──► POST /v1/files (multipart, один запрос)
│
└─ файл > 16 МиБ ──► uploads()->send()
├─ sha256 файла ──► такое уже есть в бакете? → готово, ничего не передаём
├─ POST /v1/uploads открыть сессию
├─ PATCH /v1/uploads/{id} × N части по 8 МиБ; сбой → спросить сервер, продолжить
└─ POST /v1/uploads/{id}/complete собрать файл
│
▼
ответ сервера ──► проверка полей ──► File (или исключение по статусу ответа)Вы вызываете одно и то же и получаете один и тот же File — разница только внутри.
Подробно — в разделе Конвейер загрузки.
Ошибки — по типу
Любой отказ сервера становится исключением определённого класса — по HTTP-статусу ответа.
Все они наследуют S5wException:
try {
$s5w->files()->move($file, 'archive');
} catch (NotFoundException) {
// файла уже нет
} catch (ValidationException $e) {
// нет такой папки; $e->errors() — ошибки по полям
} catch (ConflictException) {
// в папке уже есть файл с таким именем
}Отдельно от отказов сервера — TransportException (сервер вообще не ответил) и
ResponseException (ответил, но не как S5W). Полная иерархия — в справочнике
Исключения, практика — в руководстве
Обработка ошибок.
Какой уровень API выбрать
| Задача | Чем пользоваться |
|---|---|
| загрузить файл, любого размера | files()->upload() / uploadContent() |
| показать прогресс, сохранить id сессии, продолжить после перезапуска | uploads()->send() / resume() |
| файл приходит кусками (например, пересылаете поток) или нужен свой цикл | uploads()->start() / append() / complete() |
В девяти случаях из десяти достаточно первой строки. Остальные уровни — для задач, где загрузку нужно контролировать: Большие файлы и Загрузка по шагам.