В якості шаблону для генерації JPG зображень я обрав саме SVG через простоту та швидкість роботи з цим форматом. Наприклад HTML в якості шаблону дає більше можливостей, але більш складний у реалізації та підтримці.
Задача - генерація JPG картинки для OpenGraph розмітки (це картинка/прев'юшка для деяких соц. мереж). Rails застосунок знаходиться на Heroku. Кількість RAM не дуже велика, тож задачу генерації картинки треба кудись винести. Налаштовувати та платити за sidekiq для пет-проєкту немає сенсу. Гарним варіантом для мене виявився Heroku Scheduler. Цей стандартний (безкоштовний) addon буде запускати нашу рейк-таску за розкладом (раз на добу). Ну а сама таска буде знаходити пости, які ще не мають OG картинки, та будуть її генерувати та приатачувати (використовується ActiveStorage, зображення буде завантажене на aws) до моделі.
Перше що треба зробити - це зробити SVG шаблон. Спочатку граємось з SVG. OG картинка має бути 1200px x 630px.
Ось приблизно так буде виглядати SVG шаблон:
Цей шаблон не має ніяких змінних. Змінні будуть інтерпретуватись у .erb шаблоні. Закинемо цей SVG-шаблон до нашого rails-застосунка та додамо розширення .erb, наприклад lib/templates/og_image_template.svg.erb.
Я використовую бібліотеку mini_magick. Тож вже маю в Gemfile:
gem "mini_magick"
Далі сам сам код lib/cover_image_generator.rb.
require 'mini_magick'
require 'erb'
module CoverImageGenerator
class Generator
def generate_and_attach(entry, entry_title)
begin
# Динамічно заповнити SVG-шаблон
@entry_title = entry_title.truncate(160)
@blog_name = entry.blog.slug
svg_template = File.read(File.expand_path('./templates/cover_image_template.svg.erb', __dir__))
svg_content = ERB.new(svg_template).result(binding)
# Створити зображення MiniMagick з вмісту SVG
cover_image = MiniMagick::Image.read(svg_content)
# Конвертувати в JPG
cover_image.format('jpg')
temp_file_path = entry.slug + '.jpg'
cover_image.write(temp_file_path)
# Прикріпити зображення до запису
entry.cover_image.attach(io: File.open(temp_file_path), filename: "#{entry.slug}.jpg", content_type: 'image/jpeg')
# Очистити тимчасовий файл
File.delete(temp_file_path)
rescue => e
# Тут скоріш за все будуть проблеми з текстом, який має специфічні символи, які не можуть будуть конвертовані у JPG.
# Поки що такі тайтли я просто ігнорую - треба знайти рішення для цієї проблеми.
# Наразі, якщо модель немає доданого OG-зображення, я показую дефолтну картинку.
puts "Помилка на: #{@entry_title}, ID: #{entry.id}"
puts e
puts "=========================================="
end
puts "Зображення прикріплено до: #{@entry_title}, ID: #{entry.id}"
puts "=========================================="
end
end
end
AWS вже налаштован й все що треба зробити - це додати cover_image до нашої моделі (наприклад Topic)
Цей код треба додати в рейк таску, яка пройду та згенерує OG-зображення для всіх потрібних об'єктів. Також можна використовувати цей код для генерації зображення після створення або оновлення (заголовку) об'єкту.
Результат:
Приклад створеного JPEG з SVG шаблону
Цей приклад коду / концепції не є фінальним варіантом. Є ще над чим попрацювати.
Поширити цей допис
Цитувати допис
Оберіть та скопіюйте потрібний стандарт цитування: