Помоги как мне сформировать публичный url для каждой фотки чтобы я мог перейти по ссылке и открыть или загрузить фотку. using UnityEngine;
using UnityEngine.Networking;
using System.Collections;
using System.Text;
using System.Collections.Generic;
using Newtonsoft.Json;
using System.IO;

public class SelectelStorageManager : MonoBehaviour
{
    [SerializeField] private string username = "ВАШ_ЛОГИН";
    [SerializeField] private string password = "ВАШ_ПАРОЛЬ";
    [SerializeField] private string accountId = "ВАШ_ACCOUNT_ID";
    [SerializeField] private string projectName = "ВАШ_ПРОЕКТ";
    [SerializeField] private string pool = "ru-1"; // Или другой пул, если используется другой регион
    
    private string keystoneToken;
    private string projectId;

    private const string containerName = "image-storage"; // Ваш контейнер

    // Метод для инициализации аутентификации
    public void Start()
    {
        StartCoroutine(GetKeystoneToken());
    }

    // Метод для загрузки файла и получения публичного URL
    public IEnumerator UploadFileAndGetUrl(string filePath, System.Action<string> onSuccess, System.Action<string> onError)
    {
        // Убеждаемся, что токен получен
        if (string.IsNullOrEmpty(keystoneToken) || string.IsNullOrEmpty(projectId))
        {
            Debug.LogError("Необходима аутентификация. Вызовите Initialize() перед загрузкой файлов.");
            onError?.Invoke("Аутентификация не выполнена.");
            yield break;
        }

        byte[] fileData = File.ReadAllBytes(filePath);
        string fileName = Path.GetFileName(filePath);

        // Загрузка файла
        string storageUrl = $"https://swift.{pool}.storage.selcloud.ru/v1/{projectId}/{containerName}/{fileName}";

        UnityWebRequest www = new UnityWebRequest(storageUrl, "PUT");
        www.uploadHandler = new UploadHandlerRaw(fileData);
        www.downloadHandler = new DownloadHandlerBuffer();
        www.SetRequestHeader("X-Auth-Token", keystoneToken);
        www.SetRequestHeader("Content-Type", "application/octet-stream");

        yield return www.SendWebRequest();

        if (www.result != UnityWebRequest.Result.Success)
        {
            Debug.LogError("Ошибка загрузки файла: " + www.error);
            onError?.Invoke($"Ошибка загрузки файла: {www.error}");
        }
        else
        {
            Debug.Log("Файл успешно загружен в контейнер.");

            // Формируем публичный URL
            
            onSuccess?.Invoke("213");
        }
    }

    private IEnumerator GetKeystoneToken()
    {
        string authUrl = "https://api.selvpc.ru/identity/v3/auth/tokens";

        var authRequest = new AuthRequest
        {
            auth = new Auth
            {
                identity = new Identity
                {
                    methods = new List<string> { "password" },
                    password = new Password
                    {
                        user = new User
                        {
                            name = username,
                            domain = new Domain { name = accountId },
                            password = password
                        }
                    }
                },
                scope = new Scope
                {
                    project = new Project
                    {
                        name = projectName,
                        domain = new Domain { name = accountId }
                    }
                }
            }
        };

        string jsonData = JsonConvert.SerializeObject(authRequest);

        UnityWebRequest www = new UnityWebRequest(authUrl, "POST");
        byte[] bodyRaw = Encoding.UTF8.GetBytes(jsonData);
        www.uploadHandler = new UploadHandlerRaw(bodyRaw);
        www.downloadHandler = new DownloadHandlerBuffer();
        www.SetRequestHeader("Content-Type", "application/json");

        yield return www.SendWebRequest();

        if (www.result != UnityWebRequest.Result.Success)
        {
            Debug.LogError("Ошибка аутентификации: " + www.error);
        }
        else
        {
            keystoneToken = www.GetResponseHeader("X-Subject-Token");
            if (string.IsNullOrEmpty(keystoneToken))
            {
                Debug.LogError("Не удалось получить токен из ответа.");
            }
            else
            {
                Debug.Log("Keystone токен успешно получен.");

                // Парсим тело ответа, чтобы извлечь project_id
                string responseText = www.downloadHandler.text;
                var tokenResponse = JsonConvert.DeserializeObject<TokenResponse>(responseText);
                projectId = tokenResponse.token.project.id;
                if (!string.IsNullOrEmpty(projectId))
                {
                    Debug.Log("Project ID: " + projectId);
                }
                else
                {
                    Debug.LogError("Не удалось получить Project ID из ответа.");
                }
            }
        }
    }


    // Классы для сериализации и десериализации JSON данных
    [System.Serializable]
    private class AuthRequest
    {
        public Auth auth;
    }

    [System.Serializable]
    private class Auth
    {
        public Identity identity;
        public Scope scope;
    }

    [System.Serializable]
    private class Identity
    {
        public List<string> methods;
        public Password password;
    }

    [System.Serializable]
    private class Password
    {
        public User user;
    }

    [System.Serializable]
    private class User
    {
        public string name;
        public Domain domain;
        public string password;
    }

    [System.Serializable]
    private class Domain
    {
        public string name;
    }

    [System.Serializable]
    private class Scope
    {
        public Project project;
    }

    [System.Serializable]
    private class Project
    {
        public string name;
        public Domain domain;
    }

    [System.Serializable]
    private class TokenResponse
    {
        public Token token;
    }

    [System.Serializable]
    private class Token
    {
        public ProjectInfo project;
    }

    [System.Serializable]
    private class ProjectInfo
    {
        public string id;
    }
} вот дока может в старой будет инфа  Работа с файлами
Скачивание файла из контейнера
Параметры запроса
Тип запроса	URI	Заголовки	Описание
GET	https://api.selcdn.ru/v1/SEL_*****/container/file	X-Auth-Token — токен авторизации (обязателен только при скачивании из приватного контейнера; из публичных контейнеров файлы можно скачивать без токена). Также в запросе можно использовать стандартные HTTP-заголовки, описанные в RFC2616: If-Match, If-None-Match, If-Modified-Since, If-Unmodified-Since	Скачивает указанный файл
Пример запроса
curl -O https://api.selcdn.ru/v1/SEL_*****/images/image1.png
Для того чтобы открыть файл в браузере введите команду:

https://*****.selcdn.ru/container/1111_Union.patch.xml
Ответ будет выглядеть следующим образом:

HTTP/2 200
accept-language: bytes
access-control-allow-origin: *
access-control-expose-headers: Content-Type,Etag,X-Client,X-Timestamp,X-Trans-Id,Content-Length,Last-Modified,Accept-Ranges
content-length: 5782
content-type: application/xml
etag: "61808ed864f4c3453d329071986b04ba"
Для того чтобы скачать файл без открытия в браузере добавьте к ссылке get параметр ?filename=some_name.ext :

https://*****.selcdn.ru/container/1111_Union.patch.xml?filename=Union.patch.xml
Браузер предложит скачать файл под именем Union.patch.xml. Ответ будет выглядеть следующим образом:

HTTP/2 200
accept-language: bytes
access-control-allow-origin: *
access-control-expose-headers: Etag,X-Client,X-Timestamp,X-Trans-Id,Last-Modified,Accept-Ranges,Content-Length,Content-Type
content-disposition: attachment; filename="1111_Union.patch.xml" ← в ответе добавляется этот заголовок, который говорит браузеру скачать файл
content-length: 5782
content-type: application/xml
etag: "61808ed864f4c3453d329071986b04ba"
Загрузка файла в контейнер
Параметры запроса
Тип запроса	URI	Заголовки	Описание
PUT	https://api.selcdn.ru/v1/SEL_*****/container/file	X-Auth-Token — токен авторизации; X-Delete-At — время удаления файла в формате Unix Timestamp; X-Delete-After — cрок хранения файла (в секундах); Etag — идентификатор Etag; X-Object-Meta — метаданные загружаемого объекта	Загружает объект в указанный контейнер
Пример запроса
curl -i -XPUT https://api.selcdn.ru/v1/SEL_*****/new_container/file  -H "X-Auth-Token: $token" -H "X-Delete-After: 180"  -T "file"
При удачном выполнении запроса будет возвращён ответ с кодом 201.

Пример ответа
HTTP/1.1 100 Continue
HTTP/1.1 201 Created
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: X-Backend-Timestamp, Etag, Last-Modified, X-Object-Manifest, X-Timestamp
Content-Length: 0
Content-Type: text/html
Etag: b65ad34618e410d9d8bf624d61f8a980
Date: Thu, 15 Mar 2018 07:31:32 GMT
Удаление файла
Параметры запроса
Тип запроса	URI	Заголовки	Описание
DELETE	https://api.selcdn.ru/v1/SEL_*****/container/file	X-Auth-Token — токен авторизации	Удаляет указанный файл
Пример запроса
curl -i -XDELETE https://api.selcdn.ru/v1/SEL_*****/container/file -H "X-Auth-Token: $token"
В случае удачного выполнения запроса API возвращает ответ с кодом 204.

Пример ответа
HTTP/1.1 204 No Content
content-length: 0
content-type: text/html; charset=UTF-8
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers:
Удаление множества объектов из хранилища
Команда bulk-delete в отличие от команды delete:

позволяет удалить нескольких файлов одновременно;
позволяет удалять объекты из разных контейнеров одновременно;
работает последовательно.
Для работы этой команды подходит только токен основного пользователя.

Параметры запроса
Тип запроса	URI	Заголовки	Описание
POST	https://api.selcdn.ru/v1/SEL_******?bulk-delete=true	X-Auth-Token — токен авторизации; X-Delete-After — cрок хранения файла (в секундах)	Удаляет несколько указанных файлов одновременно
Пример запроса
curl -i -XPOST "https://api.selcdn.ru/v1/SEL_******?bulk-delete=true" -H "X-Auth-Token: $token" -H "Content-Type: text/plain" --data $"container/file1\ncontainer/file2\n"
При удачном выполнении запроса возвращается ответ с кодом 200.

Пример ответа
HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: X-Backend-Timestamp, Etag, Last-Modified, X-Object-Manifest, X-Timestamp
Content-Type: application/json; charset=utf-8
Date: Fri, 08 Jun 2018 13:37:53 GMT
Content-Length: 101
{"Number Not Found":0,"Response Status":"200 OK","Response Body":"","Errors":null,"Number Deleted":2}
Отложенное удаление с помощью X-Delete
Имеется два возможных варианта заголовков:

X-Delete-At;
X-Delete-After.
Оба варианта позволяют установить временной интервал для автоматического удаления объекта из хранилища. Отправка заголовков производится с помощью PUT и POST запросов.

X-Delete-At
В качестве значения заголовка передается значение времени (timestamp) в формате Unix Epoch. Это значение укажет серверу до какого времени следует хранить объект. Для конвертирования в человекочитаемый вид удобно использовать онлайн-конвертер.

Unix Epoch	Human View
1317070737	Mon Sep 26 20:58:57 2011 UTC
X-Delete-After
В качестве значения заголовка передается целочисленное количество секунд, по прошествии которых объект отправится на удаление. Сервер, получив это значение, преобразует его в заголовок X-Delete-At и при наступлении указанного значения времени перестает хранить объект.

Доступ к объектам, которым передали заголовок X-Delete-* прекращается после того как значение времени истекло. Сервер в ответ на запрос такого объекта будет отдавать ответ 404. Сами по себе объекты автоматически удаляются через некоторое время.

Двойной заголовок
Если объекту передать оба заголовка одновременно, то система будет считать приоритетным заголовок X-Delete-After.

Внутренние операции
Текущая реализация имеет следующие особенности:

если объект, имеющий X-Delete-At, помещается с помощью PUT в контейнер с модифицированным заголовком, то заголовок объекта будет перезаписан на заголовок контейнера;
если объект, имеющий X-Delete-At, помещается с помощью PUT и установленным заголовком X-Copy-From в контейнер, уже имеющий модифицированный заголовок, то заголовок объекта сохранится с предыдущего источника и не будет перезаписан;
при копировании объекта с установленным заголовком X-Delete-At с помощью COPY и заголовком Destination — заголовок не сохраняется;
при помещении объекта в контейнер для версионированных объектов через панель управления объектным хранилищем заголовок X-Delete-At / X-Delete-After сохранится в неизменном виде и объект будет удален в соответствии с указанным значением;
при помещении объекта в контейнер для версионированных объектов c помощью API (команда COPY c заголовком Destination), заголовок не сохраняется.
Управление HTTP-заголовками для файлов
Для всех файлов, помещаемых в хранилище, можно устанавливать HTTP-заголовки. Заголовки используются для управления кэшированием на стороне клиента (и на промежуточных прокси-серверах), а также для обработки кросс-доменных запросов (CORS).

Поддерживаются следующие заголовки:

Cache-Control
Access-Control-Allow-Origin
Access-Control-Max-Age
Access-Control-Allow-Methods
Access-Control-Allow-Credentials
Access-Control-Expose-Headers
Access-Control-Allow-Headers
Link
Content-Type
Content-Disposition (только для конечного файла)
Strict-Transport-Security (только для контейнера)
Параметры запроса
Тип запроса	URI	Заголовки	Описание
POST	https://api.selcdn.ru/v1/SEL_*****/container	X-Auth-Token — токен авторизации; X-Container-Meta-…. — заголовок, параметры которого нужно установить (сам заголовок указывается после -Meta-, например: X-Container-Meta-Cache-Control	Устанавливает значения для указанных в запросе заголовков
Пример запроса
curl -i -XPOST  https://api.selcdn.ru/v1/SEL_*****/container -H "X-Auth-Token: $token" \
-H "X-Container-Meta-Access-Control-Allow-Methods: HEAD, GET" \
-H "X-Container-Meta-Cache-Control: public" \
-H "X-Container-Meta-Strict-Transport-Security: max-age=31536000; includeSubDomains"
В случае удачного выполнения запроса API возвращает ответ с кодом 202.

Для добавления заголовка Link к объекту file1 введите:

curl -i -XPOST https://api.selcdn.ru/v1/SEL_*****/container/file1 -H "X-Auth-Token: $token" -H "Link: rel=canonical"
Чтобы добавить данный заголовок сразу ко всем объектам в контейнере, используется запрос:

curl -i -XPOST https://api.selcdn.ru/v1/SEL_*****/container -H "X-Auth-Token: $token" -H "X-Container-Meta-Link: rel=canonical"
Пример ответа
HTTP/1.1 202 Accepted
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: X-Backend-Timestamp, Etag, Last-Modified, X-Object-Manifest, X-Timestamp
Content-Length: 76
Content-Type: text/html
Просмотр заголовков конкретного объекта
Для просмотра заголовков конкретного объекта введите:

curl --head https://api.selcdn.ru/v1/SEL_*****/images/file.png
Поддержка больших объектов
В хранилище нет ограничений на размер загружаемых файлов, но файлы размером более 100 МБ не рекомендуется помещать в хранилище целиком — нужно использовать сегментированную загрузку.

Существует два варианта сегментированной загрузки: динамическая и статистическая.

Динамическая загрузка
В динамической загрузке объект разбивается на сегменты, после чего создается манифест — пустой файл, содержащий указатель на контейнер, в который загружены все сегменты. В запросе на загрузку большого объекта путь к контейнеру указывается в заголовке X-Object-Manifest.

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

Пример
curl -X PUT -H "X-Auth-Token: $token"  https://api.selcdn.ru/v1/SEL_*****/new_container/big_object/00000001 --data-binary "1" \
curl -X PUT -H "X-Auth-Token: $token"  https://api.selcdn.ru/v1/SEL_*****/new_container/big_object/00000002 --data-binary "2" \
curl -X PUT -H "X-Auth-Token: $token"  https://api.selcdn.ru/v1/SEL_*****/new_container/big_object/00000003 --data-binary "3"
Пример манифеста
curl -X PUT -H "X-Auth-Token: $token" -H "X-Object-Manifest: new_container/big_object/" https://api.selcdn.ru/v1/SEL_*****/new_container/big_object --data-binary ""
Статическая загрузка
При статической загрузке нужно создать статический манифест с указанием путей до сегментов, их контрольной суммы (Etag) и размера. Манифест сохраняется в специальном файле.

Пример манифеста
[{"path": "/cont/object",  "etag": "etagoftheobjectsegment",  "size_bytes": 10485760, }, ...]
Файл манифеста нужно загрузить в хранилище PUT-запросом с query-параметром ?multipart-manifest=put и заголовком X-Static-Large-Object: True.

Чтобы получить файл манифеста, нужно выполнить GET-запрос с query-параметром ?multipart-manifest=get.

Удалить все сегменты, а затем файл с манифестом можно с помощью DELETE-запроса с query-параметром ?multipart-manifest=delete.

Установка срока хранения для файлов
Параметры запроса
Тип запроса	URI	Заголовки	Описание
PUT	https://api.selcdn.ru/v1/SEL_*****/container	X-Auth-Token — токен авторизации; X-Container-Meta-Default-Delete-After — cрок хранения для всех файлов в контейнере (в секундах)	Устанавливает срок хранения для всех файлов, помещаемых в контейнер. По истечении этого срока все файлы будут автоматически удалены
Пример запроса
curl -i -XPUT https://api.selcdn.ru/v1/SEL_*****/container  -H "X-Auth-Token: $token" \
-H "X-Container-Meta-Default-Delete-After: 300"
При удачном выполнении запроса будет возвращён ответ с кодом 202.

Пример ответа
HTTP/1.1 202 Accepted
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: X-Backend-Timestamp, Etag, Last-Modified, X-Object-Manifest, X-Timestamp
Content-Length: 76
Content-Type: text/html
Установка метаданных файла
Параметры запроса
Тип запроса	URI	Заголовки	Описание
POST	https://api.selcdn.ru/v1/SEL_*****/container/file	X-Auth-Token — токен авторизации; X-Object-Meta-Some — любая дополнительная информация о файле	Добавляет в файл метаданные, переданные в заголовке X-Object-Meta-Some
Пример запроса
curl -i -XPOST  https://api.selcdn.ru/v1/SEL_***/new_container/file -H "X-Auth-Token: $token" -H "X-Object-Meta-Some: metadata"
В случае успешного выполнения запроса API возвращает ответ с кодом 201.

Пример ответа
HTTP/1.1 201 Created
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: X-Backend-Timestamp, Etag, Last-Modified, X-Object-Manifest, X-Timestamp
Content-Length: 0
Content-Type: text/html
Etag: d41d8cd98f00b204e9800998ecf8427e