Доброго времени суток, Хабр!

Несколько лет назад, я участвовал в разработке одного проекта, и как задачу, мне (тогда еще зеленому джуну) поставили реализовать систему загрузки медиа файлов на AWS S3. Важным условием было реализовать конкретно SignedURL.

Спустя несколько лет, я опять столкнулся с такой же задачей, но заказчик настаивал на отечественном облачном хранилище.

Ниже я немного расскажу что вообще такое подписанные ссылки, как перейти на Yandex Object Storage(YOS), и почему YOS и AWS S3 - это одно и то же!


Оптимизация клиент/сервера при работе с бинарными файлами

Рис 1 - Варианты загрузки файлов в облако при клиент-серверной архитектуре
Рис 1 - Варианты загрузки файлов в облако при клиент-серверной архитектуре

Что за такие подписанные ссылки?

Это URL, содержащий данные для авторизации запроса в своих параметрах.

То бишь, отправляя любой бинарный файл по определенной ссылке, он автоматически будет загружен в облако после валидации параметров из url.

В чем же преимущество?

В случае классической архитектуры, пользователь, загружает файл сперва на клиент, далее клиент отправляет запрос с файлом в body на сервер, и только после это сервер отправляет тот же файл в облако.

Получается что один файл мы загружаем из одного элемента структуры в другой 3 раза.
Конечно если файл маленький по объему - то простоя не будет, да и сервер сильно нагружаться не будет.
Но что если это длинное видео, в хорошем разрешении???

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

Соответственно получается так, что загрузка проходит всего 2 раза от пользователя на клиент, и с клиента в облако. Сокращаем время работы 1 операции в +- 1.5 раза.


Yandex Object Storage

Начал я работу над задачей собственно с регистрации на Yandex Cloud, слава, что концепция такая же что и у AWS.

  • Регистрация на Yandex Cloud.

  • Создание отдельного облака.

Рис 2 - Интерфейс создания облака на YOS
Рис 2 - Интерфейс создания облака на YOS
  • Добавляем "Сервисный аккаунт" - проще говоря цифровой пользователь, содержащий статический и приватный ключи доступа. Для сервисного аккаунта можно указать его роли в каталоге, разделяя таким образом области доступа при работе с YOS.

Важно: Не забудьте после создания сервисного аккаунта добавить ему статический ключ доступа и сохранить их!

Рис 3 - Интерфейс создания сервисного аккаунта
Рис 3 - Интерфейс создания сервисного аккаунта
  • Далее само хранилище (bucket)

На этом считайте что работа с сайтом закончена, дальше будет код, код и еще раз код.


AWS SDK и Yandex Object Storage

Думая над технической реализацией я прикинул, что суть подписанных url от места хранения не меняется. И ведь прав был, посмотрев в документации AWS S3 и YOS как генерируется ссылка отличий я не заметил.

Тут и было принято решение использовать уже знакомый мне SDK для NodeJS от Амазона.

  • Инициализировал AWS, все ключи прописал в .env, а регион у яндекса всего 1 - ru-central1

import AWS from 'aws-sdk';
import { AWS_REGION, AWS_USER_KEY, AWS_USER_SECRET_KEY } from '../../config';

const accessKeyId = AWS_USER_KEY;
const secretAccessKey = AWS_USER_SECRET_KEY;

AWS.config.update({ region: AWS_REGION, accessKeyId, secretAccessKey });

export default AWS;
  • После собственно, создаем сервис для работы с API

const client = new aws.S3({
  endpoint: 'https://storage.yandexcloud.net',
});
  • В задаче стоит необходимость получать подписанную ссылку, по которой клиент сможет загрузить файл в облако, это и реализовал.

export const getSignedUrl = async ({ type }: GetSignedUrlInput): Promise<GetSignedUrlResponse> => {
  const action = 'putObject';
  let objectKey = cuid();

  let params = {
    Bucket: AWS_BUCKET_NAME,
    Key: `${objectKey}.${mime.extension(type)}`,
    ContentType: type,
    Expires: Number(SIGN_URL_EXPIRES),
    ACL: 'public-read',
  };

  const signedURL: string = await new Promise((resolve, reject) => {
    client.getSignedUrl(action, params, (err, url) => {
      if (err) {
        reject(err);
      } else {
        resolve(url);
      }
    });
  });

  return { signedURL, objectURL: `https://${AWS_BUCKET_NAME}.storage.yandexcloud.net/${params.Key}`, expensive: params.Expires };
};

Теперь поясню что тут происходит:

  1. Создаем функцию, которую будем вызывать в последствии для получения ссылки, что в нее передавать - ваше решение, мне хватило только типа загружаемого файла. Возвращать собственно нужно как минимум подписанную ссылку

  2. Далее я создаю объект с параметрами, по которым будет строиться ссылка, из важных только Bucket и Key, остальные поля опциональны

  3. По объекту параметров генерируем ссылку с помощью функции из AWS SDK.

  4. PROFIT!!! Все готово)

Теперь что касается клиентской части.

Ссылка содержит в себе все необходимы headers для успешной аутентификации.

Стоит заметить что тип запроса при отправке на YOS должен быть PUT, информации про это в документации я не нашел, а вот у AWS S3 тип запроса POST.


Заключение

Это моя первая статья, еще утром я и не думал её писать, а вечером вышло это.

Для чего я это написал? Просто что бы помочь тем кто столкнется с такой задачей, ибо информации по использованию YOS на NodeJS мало, и той что помогла бы мне я не нашел.

Всем спасибо кто дочитал до этого момента!

Комментарии (7)


  1. Tanner
    26.11.2022 04:46

    Зачем менять один вендорлок на другой, когда можно самому захостить minio?


    1. vitalyisaev2
      26.11.2022 06:41

      Возможно, потому, что Минио не особо юзабелен https://highload.ru/spb/2022/abstracts/9072


      1. Tanner
        26.11.2022 12:39

        Насколько я понял, доклад по ссылке опровергает ваш комментарий – решение-то внедрено.


  1. DinoZavr2
    26.11.2022 10:31

    Свежий, полезный пост из песочницы :) пиши ещё подобного


  1. ToTheHit
    26.11.2022 23:58
    +1

    Если использовать SignedURL для загрузки файлов, то как нам быть уверенными, что клиент залил нужный формат/размер/...?

    В случае классической архитектуры, пользователь, загружает файл сперва на клиент, далее клиент отправляет запрос с файлом в body на сервер, и только после это сервер отправляет тот же файл в облако."

    В "классической архитектуре" используются стримы и сервера не сохраняют файл у себя, при этом остаётся возможность выцепить из стрима мета данные этого файла.


    1. SirToxin Автор
      27.11.2022 00:02

      В случае необходимости обрабатывать медиа файлы использование SignedURL в некоторой степени бессмысленно(В случае если Вы хотите обработать медиа у себя на сервере).
      Проверку формата можно легко добавить на клиент.
      А если вы хотите использовать и SignedURL и при этом быть уверенными в формате/размере, да еще и обработать файл, можно использовать AWS Lambda.


  1. investopedia
    26.11.2022 23:58

    intersting info