Доброго времени суток, Хабр!
Несколько лет назад, я участвовал в разработке одного проекта, и как задачу, мне (тогда еще зеленому джуну) поставили реализовать систему загрузки медиа файлов на AWS S3. Важным условием было реализовать конкретно SignedURL.
Спустя несколько лет, я опять столкнулся с такой же задачей, но заказчик настаивал на отечественном облачном хранилище.
Ниже я немного расскажу что вообще такое подписанные ссылки, как перейти на Yandex Object Storage(YOS), и почему YOS и AWS S3 - это одно и то же!
Оптимизация клиент/сервера при работе с бинарными файлами
Что за такие подписанные ссылки?
Это URL, содержащий данные для авторизации запроса в своих параметрах.
То бишь, отправляя любой бинарный файл по определенной ссылке, он автоматически будет загружен в облако после валидации параметров из url.
В чем же преимущество?
В случае классической архитектуры, пользователь, загружает файл сперва на клиент, далее клиент отправляет запрос с файлом в body на сервер, и только после это сервер отправляет тот же файл в облако.
Получается что один файл мы загружаем из одного элемента структуры в другой 3 раза.
Конечно если файл маленький по объему - то простоя не будет, да и сервер сильно нагружаться не будет.
Но что если это длинное видео, в хорошем разрешении???
В случае же подписанных ссылок, файл загружается напрямую с клиента на облако, обходя сервер.
Соответственно получается так, что загрузка проходит всего 2 раза от пользователя на клиент, и с клиента в облако. Сокращаем время работы 1 операции в +- 1.5 раза.
Yandex Object Storage
Начал я работу над задачей собственно с регистрации на Yandex Cloud, слава, что концепция такая же что и у AWS.
Регистрация на Yandex Cloud.
Создание отдельного облака.
Добавляем "Сервисный аккаунт" - проще говоря цифровой пользователь, содержащий статический и приватный ключи доступа. Для сервисного аккаунта можно указать его роли в каталоге, разделяя таким образом области доступа при работе с YOS.
Важно: Не забудьте после создания сервисного аккаунта добавить ему статический ключ доступа и сохранить их!
Далее само хранилище (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 };
};
Теперь поясню что тут происходит:
Создаем функцию, которую будем вызывать в последствии для получения ссылки, что в нее передавать - ваше решение, мне хватило только типа загружаемого файла. Возвращать собственно нужно как минимум подписанную ссылку
Далее я создаю объект с параметрами, по которым будет строиться ссылка, из важных только Bucket и Key, остальные поля опциональны
По объекту параметров генерируем ссылку с помощью функции из AWS SDK.
PROFIT!!! Все готово)
Теперь что касается клиентской части.
Ссылка содержит в себе все необходимы headers для успешной аутентификации.
Стоит заметить что тип запроса при отправке на YOS должен быть PUT, информации про это в документации я не нашел, а вот у AWS S3 тип запроса POST.
Заключение
Это моя первая статья, еще утром я и не думал её писать, а вечером вышло это.
Для чего я это написал? Просто что бы помочь тем кто столкнется с такой задачей, ибо информации по использованию YOS на NodeJS мало, и той что помогла бы мне я не нашел.
Всем спасибо кто дочитал до этого момента!
Комментарии (7)
ToTheHit
26.11.2022 23:58+1Если использовать SignedURL для загрузки файлов, то как нам быть уверенными, что клиент залил нужный формат/размер/...?
В случае классической архитектуры, пользователь, загружает файл сперва на клиент, далее клиент отправляет запрос с файлом в body на сервер, и только после это сервер отправляет тот же файл в облако."
В "классической архитектуре" используются стримы и сервера не сохраняют файл у себя, при этом остаётся возможность выцепить из стрима мета данные этого файла.
SirToxin Автор
27.11.2022 00:02В случае необходимости обрабатывать медиа файлы использование SignedURL в некоторой степени бессмысленно(В случае если Вы хотите обработать медиа у себя на сервере).
Проверку формата можно легко добавить на клиент.
А если вы хотите использовать и SignedURL и при этом быть уверенными в формате/размере, да еще и обработать файл, можно использовать AWS Lambda.
Tanner
Зачем менять один вендорлок на другой, когда можно самому захостить minio?
vitalyisaev2
Возможно, потому, что Минио не особо юзабелен https://highload.ru/spb/2022/abstracts/9072
Tanner
Насколько я понял, доклад по ссылке опровергает ваш комментарий – решение-то внедрено.