Перейти к содержимому

Обычные файлы ​

Путь файла в репозитории работает как файл на веб-сервере. PUT сохраняет тело запроса как следующую версию пути. GET возвращает текущую версию. Используйте это в сценариях сборки и заданиях CI, где есть curl или PowerShell и больше ничего.

Адрес один и тот же для всех трёх методов:

text
https://<хост>/api/v1/repositories/<репозиторий>/raw/<путь>

Например: https://arkvory.example/api/v1/repositories/releases/raw/builds/game/1.4/GameSetup.exe.

Сохранение файла ​

Вам нужны репозиторий и ключ с доступом на запись. См. Учётные записи и ключи. Передавайте ключ как Authorization: Bearer <ключ>. Другие виды аутентификации для обычных файлов не принимаются.

bash
export ARKVORY_KEY="$(cat ~/.arkvory/key)"
URL="https://arkvory.example/api/v1/repositories/releases/raw/builds/game/1.4/GameSetup.exe"

curl --fail-with-body -sS -T ./GameSetup.exe \
  -H "Authorization: Bearer $ARKVORY_KEY" \
  "$URL"
powershell
$headers = @{ Authorization = "Bearer $env:ARKVORY_KEY" }
$url = 'https://arkvory.example/api/v1/repositories/releases/raw/builds/game/1.4/GameSetup.exe'
Invoke-RestMethod -Method Put -InFile .\GameSetup.exe -Headers $headers -Uri $url
bash
wget -qO- --method=PUT --body-file=GameSetup.exe \
  --header="Authorization: Bearer $ARKVORY_KEY" "$URL"

Дайте curl -T полный адрес файла, а не папки. Кодируйте в пути символы, которые не допускаются в URL: пробел записывайте как %20, # — как %23, а ? — как %3F.

Ответ — JSON. Новый файл или новые байты возвращают 201:

json
{
  "path": "builds/game/1.4/GameSetup.exe",
  "revision": 1,
  "created": true,
  "artifact": { "id": "00000000-0000-4000-8000-000000000001", "size": "1048576", "sha256": "…" }
}

Если по пути уже лежат именно эти байты, ответ — 200 с "created": false и той же версией. Ничего не сохраняется. Шаг задания CI можно запустить снова, не создавая новую версию. size — строка из десятичных цифр.

Отправка контрольной суммы ​

Отправьте SHA-256 файла в заголовке X-Checksum-Sha256, а длину — в Content-Length. curl -T и PowerShell отправляют длину для файла. Тогда сервер записывает байты прямо в хранилище за один проход и проверяет их там. Неверная контрольная сумма возвращает 422 с кодом integrity_mismatch, ничего не сохраняет и оставляет путь прежним.

bash
curl --fail-with-body -sS -T ./GameSetup.exe \
  -H "Authorization: Bearer $ARKVORY_KEY" \
  -H "X-Checksum-Sha256: $(sha256sum GameSetup.exe | cut -d' ' -f1)" \
  "$URL"
powershell
$headers['X-Checksum-Sha256'] = (Get-FileHash .\GameSetup.exe -Algorithm SHA256).Hash.ToLower()
Invoke-RestMethod -Method Put -InFile .\GameSetup.exe -Headers $headers -Uri $url

Без контрольной суммы или с chunked-телом без Content-Length сервер сначала записывает тело во временный файл и вычисляет его хеш. Затем он сохраняет файл. На короткое время для этого нужно до удвоенного размера файла на диске сервера и второй проход по байтам. Временные файлы, оставшиеся после сбоя, сервер удаляет через сутки.

Если вы отправляете контрольную сумму и длину, а по пути уже лежат эти байты, сервер отвечает 200, не читая тело, и закрывает соединение.

Только создание ​

PUT учитывает только одно условие — If-None-Match: *. С ним сервер сохраняет файл, только если пути не существует. Иначе он отвечает 409 с причиной already_exists, даже если байты те же. Любое другое значение If-None-Match в PUT возвращает 400.

bash
curl --fail-with-body -sS -T ./notes.txt \
  -H "Authorization: Bearer $ARKVORY_KEY" -H "If-None-Match: *" \
  "https://arkvory.example/api/v1/repositories/releases/raw/docs/notes.txt"

Две одновременные записи ​

Когда два запроса одновременно меняют один путь, побеждает первый. Второй получает 409 с причиной revision_mismatch, а путь сохраняет содержимое победителя. Запустите запрос снова, чтобы создать новую версию. Загруженные байты проигравшего остаются артефактом без пути, пока их не удалят правила хранения.

Чтение файла ​

GET возвращает текущую версию пути. HEAD возвращает только заголовки.

bash
curl --fail-with-body -sS -H "Authorization: Bearer $ARKVORY_KEY" -o GameSetup.exe "$URL"
powershell
$ProgressPreference = 'SilentlyContinue'
Invoke-WebRequest -Headers $headers -Uri $url -OutFile .\GameSetup.exe
bash
wget --header="Authorization: Bearer $ARKVORY_KEY" -O GameSetup.exe "$URL"

Заголовки ответа:

ЗаголовокЗначение
ETag"sha256:<дайджест>": SHA-256 содержимого в кавычках
Content-LengthРазмер файла
Accept-Rangesbytes
Content-TypeВсегда application/octet-stream
Content-Dispositionattachment с последним сегментом пути в качестве имени файла
X-Arkvory-Artifact-IdID артефакта, в котором хранится эта версия

Неизвестный путь возвращает 404.

Диапазоны и условные запросы ​

Заголовок запросаДействие
Range: bytes=0-1023206 с запрошенной частью и Content-Range. Начало за концом файла возвращает 416 с Content-Range: bytes */<размер>.
Range: bytes=-1024Последние 1024 байта
Range: bytes=1048576-От смещения до конца
If-Range: "sha256:…"Применяет Range, только если ETag в точности такой. Если у пути новая версия, вы получаете весь новый файл.
If-None-Match: "sha256:…"304 без тела, если ETag тот же. Работает и с HEAD.

В одном запросе поддерживается только один диапазон. Запрос с несколькими диапазонами возвращает весь файл.

У пути в любой момент может появиться новая версия, а GET каждый раз разрешает путь заново. Чтобы безопасно продолжить скачивание, запомните ETag первого ответа и отправьте его в If-Range:

bash
etag=$(curl -sSI -H "Authorization: Bearer $ARKVORY_KEY" "$URL" | awk 'tolower($1)=="etag:" {print $2}' | tr -d '\r')
curl -sS -H "Authorization: Bearer $ARKVORY_KEY" -H "If-Range: $etag" \
  -H "Range: bytes=1048576-" "$URL" >> GameSetup.part

Чтобы пропустить скачивание, если файл не изменился, отправьте ETag, который вы сохранили в прошлый раз:

bash
curl -sS -o /dev/null -w '%{http_code}\n' -H "Authorization: Bearer $ARKVORY_KEY" \
  -H 'If-None-Match: "sha256:<дайджест>"' "$URL"

Для больших файлов arkvoryctl get скачивает с возможностью продолжения и сам проверяет SHA-256.

Пути и версии ​

Обычный файл — это файл по пути. Каждый PUT с новыми байтами добавляет версию пути: версия 1, 2, 3 и так далее. Прежние версии остаются. Байты никогда не заменяются, потому что каждая версия указывает на собственный неизменяемый артефакт, названный по последнему сегменту пути.

  • GET по обычному адресу всегда отдаёт текущую версию.
  • Чтобы увидеть все версии пути, прочитайте его историю: getAssetHistory или История файлов в консоли.
  • Чтобы прочитать более старую версию, getAssetRevision возвращает её артефакт. Скачайте его по адресу содержимого артефакта.
  • Чтобы вернуться к старой версии, используйте restoreAsset. Она добавляет новую версию, которая указывает на старые байты.
  • Чтобы перечислить пути репозитория по префиксу, используйте listAssetPage.
  • Путь нельзя удалить. История остаётся. Правила хранения не удаляют артефакты, которые использует версия пути.

Те же операции есть в SDK (client.raw.putRawFile, downloadRawFile) и в arkvoryctl (put, get).

Правила путей ​

ПравилоЗначение
ДлинаОт 1 до 1024 символов
ПапкиСегменты, разделённые /
Не допускаютсяПустой сегмент (a//b), . или .., обратная косая черта, двоеточие и управляющие символы

curl и браузеры убирают . и .. из URL перед отправкой, поэтому такой путь никогда не доходит до сервера. Путь, нарушающий правила, возвращает 400.

Права ​

Персональные токены и файловые ключи получают доступ на чтение или запись к репозиторию. Ключи сервиса получают точные действия.

ОперацияДействия ключа сервисаПерсональный токен или файловый ключ
GET, HEADcontent.readДоступ на чтение
PUTupload.create, upload.write, upload.complete, asset.read, asset.write, artifact.readДоступ на запись, область токена read-write

Агенту развёртывания, который только скачивает, нужно действие content.read.

Шлюз чтения принимает только GET и HEAD. Зеркало обслуживает чтение и отклоняет PUT с 409 и причиной mirror_read_only.

Ограничения ​

ОграничениеЗначение
Размер файлаМаксимальный размер объекта в установке, ARKVORY_MAX_OBJECT_BYTES (по умолчанию около 10 ТиБ)
Один запрос PUTДолжен завершиться в течение 30 минут и не должен простаивать дольше 30 секунд (ARKVORY_UPLOAD_DEADLINE_MS, ARKVORY_UPLOAD_IDLE_TIMEOUT_MS). Это же наибольшие допустимые значения.
Одновременные загрузкиПо умолчанию 2 на сервер и 1 на ключ (ARKVORY_MAX_UPLOADS, ARKVORY_MAX_UPLOADS_PER_PRINCIPAL). Ожидающий запрос сдаётся через 20 секунд с ответом 503.
Одновременные скачиванияПо умолчанию 16 на сервер и 4 на ключ
КвотаФайл учитывается в квоте репозитория и в ёмкости установки

У одиночного PUT нет продолжения: после сбоя он начинается с первого байта. Используйте обычные файлы для небольших и средних файлов и для скриптов. Для больших файлов или медленных сетей используйте arkvoryctl put или SDK. Они загружают по частям, продолжают после сбоя и проверяют SHA-256. Они также сохраняют файл как версию пути. Переменные описаны в разделе Переменные окружения.

Unity Addressables ​

Addressables загружают каталог и бандлы обычными запросами GET, поэтому папка сборки может лежать по raw-пути. Это подходит для внутренних сборок, QA и инструментов. Это не подходит для игроков в открытом интернете: чтение raw всегда требует ключа, ссылка на скачивание живёт не дольше 24 часов, а ключ внутри выпущенного клиента не секрет.

Загрузите папку командой arkvoryctl put. Файлы с неизменившимися байтами повторно не отправляются. Используйте отдельную папку на каждую сборку: путь всегда отдаёт новейшую версию, а старый каталог не должен встречаться с новыми бандлами:

bash
cd ServerData/StandaloneWindows64
find . -type f | while read -r file; do
  arkvoryctl put "$file" "addressables/game/$BUILD/StandaloneWindows64/${file#./}" || exit 1
done
powershell
$root = (Resolve-Path .\ServerData\StandaloneWindows64).Path
Get-ChildItem $root -Recurse -File | ForEach-Object {
  $relative = $_.FullName.Substring($root.Length + 1) -replace '\\', '/'
  arkvoryctl put $_.FullName "addressables/game/$env:BUILD/StandaloneWindows64/$relative"
  if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
}

Укажите в профиле Addressables удалённый путь загрузки — raw-адрес этой папки, например https://arkvory.example/api/v1/repositories/releases/raw/addressables/game/<build>/[BuildTarget]. Дайте клиенту ключ только на чтение: персональный токен доступа с чтением или ключ сервиса с действием content.read. Raw принимает ключ только в заголовке Authorization, поэтому задайте его в каждом запросе:

csharp
Addressables.WebRequestOverride = request =>
{
    if (request.url.StartsWith("https://arkvory.example/"))
        request.SetRequestHeader("Authorization", "Bearer " + readKey);
};

В Addressables 1.x это свойство Addressables.WebRequestOverride; проверьте имя в своей версии. Это шаблон, а не проверенная интеграция.

Устранение неполадок ​

Ошибки — это JSON-документы с code, reason, message и requestId. См. Ошибки. Передайте requestId администратору, чтобы он нашёл запрос в журнале сервера.

СтатусReasonПричинаЧто делать
400validationПуть, Content-Length, X-Checksum-Sha256 или If-None-Match недопустимыПроверьте правила путей и закодируйте URL
401credential_missing, credential_invalid, token_expiredНет ключа, неверный ключ или токен истёкОтправляйте Authorization: Bearer <ключ>. Basic-аутентификация для обычных файлов не работает.
403permission_missing, read_only_tokenКлюч не может записывать, или это токен только для чтенияИспользуйте ключ с действиями из раздела Права
404Пути не существует, или ключ не видит репозиторийПроверьте имя репозитория и путь
409already_existsIf-None-Match: *, а путь существуетУберите заголовок, чтобы добавить версию
409revision_mismatchДругой запрос изменил путь раньшеПовторите запрос
409mirror_read_onlyРепозиторий — зеркалоЗаписывайте на основной сервер
416range_not_satisfiableДиапазон начинается после конца файлаПроверьте размер через HEAD
422integrity_mismatchТело не соответствует X-Checksum-Sha256 или Content-LengthВычислите контрольную сумму заново; проверьте прокси-сервер
503busyСлишком много одновременных передачПодождите время из Retry-After и повторите
507storage_quotaДостигнута квота репозитория или ёмкость установкиОсвободите место или запросите большую квоту
Не статусcurl: (55) или (56) при отправкеСервер закрыл соединение. Если по пути уже лежат эти байты, он отвечает 200 и закрывает соединение.Запустите curl -i и прочитайте ответ
Не статусСоединение закрывается через 30 минутПредел времени загрузкиИспользуйте arkvoryctl put

Arkvory · ProAnimaStudio