Довольно распространенная задача – создание превью картинок для сайта из полноразмерных изображений. Автоматизируем этот процесс с помощью триггера для Yandex Object Storage с функцией в Yandex Cloud Functions, которую он будет запускать с наступлением определенного события в бакете – в нашем случае, появлением в нем картинки. Функция сделает из нее превью и сохранит в соседний бакет. Возможна вариация сохранения превью в тот же бакет, но с другим префиксом.
Также с помощью триггера в Yandex.Cloud можно задавать действия не только для объектов в хранилище, но и для событий в очереди.
Триггер для Object Storage и Cloud Functions
Для решения этой задачи необходим настроенный бакет в Object Storage, в него будут загружаться файлы и в нем же будет лежать архив с кодом функции. Репозиторий с примером кода можно скачать тут. После выполнения инструкций из репозитория у вас будет готовая функция, к которой необходимо подключить триггер, срабатывающий на загрузку новых файлов.
В настройках триггера задаем его параметры: название, описание и тип – в нашем случае Object Storage. Также указываем бакет, в который будут загружаться картинки. Если дополнительно указать префикс и суффикс объекта, то можно обойтись одним бакетом и для исходных файлов, и для превью.
Далее указываем имя нашей функции, интервал повторных запросов и создаем триггер. Готово, теперь после загрузки картинке будет автоматически создаваться ее превью.
Превью на лету
Но у такого решения есть пара ограничений.
Превью создаются только для новых изображений, если картинка уже лежала в бакете, то для нее превью не будет создано.
Необходимо заранее определять высоту и ширину превью, чтобы внести изменения в соотношение сторон или размер необходимо заново масштабировать все картинки.
Воспользуемся новой возможностью Yandex.Cloud - runtime для функций — C#, чтобы доработать нашу функцию на csharp.
Теперь схема будет работать так.
При первичном обращении за превью:
Пользователь запрашивает картинку, указав в URL ее желаемые размеры.
Объектное хранилище не находит нужного файла и согласно настроенным правилам, переадресует запрос в функцию.
Функция идет за оригинальным изображением в объектное хранилище.
Масштабирует его до нужных размеров и сохраняет по ключу, содержащему эти размеры.
Сразу отдает готовый файл пользователю.
При последующих обращениях за файлом пользователь будет сразу получать файл из объектного хранилища без вызова функции.
Особенности технической реализации
Прописываем правила переадресации для бакета, чтобы Object Storage и Cloud Functions смогли работать в связке.
s3.putBucketWebsite({
Bucket: "%bucket_name%",
WebsiteConfiguration: {
IndexDocument: {
Suffix: "index.html"
},
RoutingRules: [
{
Condition: {
HttpErrorCodeReturnedEquals: "404",
KeyPrefixEquals: "images/"
},
Redirect: {
HttpRedirectCode: "302",
HostName: "functions.yandexcloud.net",
Protocol: "https",
ReplaceKeyPrefixWith: "%function_id%?path=",
}
}
]
}
})
Если объектное хранилище не найдет (HttpErrorCodeReturnedEquals: "404")
запрошенный файл с указанным KeyPrefixEquals
, то применит указанный ниже редирект. Подробнее можно посмотреть в документации к AWS S3, а скачать готовый код функции можно в репозитории тут.
В Yandex.Cloud удобно реализовано создание функций с помощью CLI. Вам не надо архивировать код и загружать его в объектное хранилище, достаточно лишь сложить все файлы в директорию и указать на нее при создании версии функции в ключе --source-path. Так же вы можете не передавать все node_modules, а загрузить только package.json и выбрать --runtime nodejs12 или --runtime nodejs14. Все зависимости будут подтянуты в момент создания версии функции.
Обращаться к файлам надо не по обычному хосту %bucket_name%.storage.yandexcloud.net
, так как редиректы обрабатываться не будут, а через %bucket_name%.website.yandexcloud.net/PREFIX/%width%x%height%/%path%
.
Например, при обращении к %bucket_name%.website.yandexcloud.net/images/500x500/cats/meow.png
вы получите картинку которую положили в бакет %bucket_name%
по ключу images/cats/meow.png
но отмасштабированную до размеров 500x500px.
Чтобы не выстрелить себе в ногу
Если не задать значение переменной окружения ALLOWED_DIMENSIONS, то в функции не будут накладываться ограничения на размеры. Это значит, что если будут запрошены много разных вариантов размеров, то все они будут созданы и загружены в объектное хранилище. Поэтому если вы точно знаете, что вам понадобятся определенные размеры картинок лучше их указать. В противном случае, вы быстро израсходуете все свободное пространство хранилища.
Так же, если у вас очень много изображений, можно задать некоторое значение TTL, создать LRU схему кэширования.
P.S.
Любые вопросы по Serverless и Yandex Cloud Functions обсуждаем у нас в Telegram: Yandex Serverless Ecosystem
Полезные ссылки: