![](https://habrastorage.org/getpro/habr/upload_files/c9e/e16/ad5/c9ee16ad5169bbf7295dd0c7ff79a01d.png)
Довольно распространенная задача – создание превью картинок для сайта из полноразмерных изображений. Автоматизируем этот процесс с помощью триггера для Yandex Object Storage с функцией в Yandex Cloud Functions, которую он будет запускать с наступлением определенного события в бакете – в нашем случае, появлением в нем картинки. Функция сделает из нее превью и сохранит в соседний бакет. Возможна вариация сохранения превью в тот же бакет, но с другим префиксом.
Также с помощью триггера в Yandex.Cloud можно задавать действия не только для объектов в хранилище, но и для событий в очереди.
Триггер для Object Storage и Cloud Functions
Для решения этой задачи необходим настроенный бакет в Object Storage, в него будут загружаться файлы и в нем же будет лежать архив с кодом функции. Репозиторий с примером кода можно скачать тут. После выполнения инструкций из репозитория у вас будет готовая функция, к которой необходимо подключить триггер, срабатывающий на загрузку новых файлов.
![](https://habrastorage.org/getpro/habr/upload_files/b1c/7ca/3d9/b1c7ca3d98606059885b88e239c03c40.png)
В настройках триггера задаем его параметры: название, описание и тип – в нашем случае Object Storage. Также указываем бакет, в который будут загружаться картинки. Если дополнительно указать префикс и суффикс объекта, то можно обойтись одним бакетом и для исходных файлов, и для превью.
![](https://habrastorage.org/getpro/habr/upload_files/e39/062/185/e390621855bfce8d576227318ab33997.png)
Далее указываем имя нашей функции, интервал повторных запросов и создаем триггер. Готово, теперь после загрузки картинке будет автоматически создаваться ее превью.
Превью на лету
![](https://habrastorage.org/getpro/habr/upload_files/321/ad2/f9a/321ad2f9a5d9f63b859cd3d58632a7c0.png)
Но у такого решения есть пара ограничений.
Превью создаются только для новых изображений, если картинка уже лежала в бакете, то для нее превью не будет создано.
Необходимо заранее определять высоту и ширину превью, чтобы внести изменения в соотношение сторон или размер необходимо заново масштабировать все картинки.
Воспользуемся новой возможностью Yandex.Cloud - runtime для функций — C#, чтобы доработать нашу функцию на csharp.
Теперь схема будет работать так.
При первичном обращении за превью:
Пользователь запрашивает картинку, указав в URL ее желаемые размеры.
Объектное хранилище не находит нужного файла и согласно настроенным правилам, переадресует запрос в функцию.
Функция идет за оригинальным изображением в объектное хранилище.
Масштабирует его до нужных размеров и сохраняет по ключу, содержащему эти размеры.
Сразу отдает готовый файл пользователю.
![](https://habrastorage.org/getpro/habr/upload_files/e93/cb3/ed9/e93cb3ed909e122dfc501d5a2f7e8db9.png)
При последующих обращениях за файлом пользователь будет сразу получать файл из объектного хранилища без вызова функции.
![](https://habrastorage.org/getpro/habr/upload_files/2cd/a8a/799/2cda8a79946e6579caa56fce4ee2d9a6.png)
Особенности технической реализации
Прописываем правила переадресации для бакета, чтобы 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
Полезные ссылки: