WorkersとCloudflare Image Resizingを使用してYouTubeサムネイルを生成する
このチュートリアルでは、Cloudflare WorkersとCloudflare Image Resizingを使用して、プログラム的にカスタムYouTubeサムネイルを生成する方法を学びます。サムネイルのデザイン、コール・トゥ・アクション、視聴者を動画に引き込むために使用する画像をカスタマイズするために、カスタムYouTubeサムネイルを生成したい場合があります。
このチュートリアルでは、Images、Image ResizingおよびCloudflare Workersを使用する方法を理解するのに役立ちます。
All of the tutorials assume you have already completed the Get started guide, which gets you set up with a Cloudflare Workers account, C3 ↗, and Wrangler.
このチュートリアルに従うには、Node、Cargo、およびWranglerがマシンにインストールされていることを確認してください。
このチュートリアルでは、以下のことを学びます:
- CloudflareダッシュボードまたはAPIを使用してCloudflareに画像をアップロードする。
- Wranglerを使用してWorkerプロジェクトをセットアップする。
- Worker内で画像変換を使用して画像を操作する。
カスタムサムネイル画像を生成するには、まずCloudflare Imagesに背景画像をアップロードする必要があります。これがサムネイルを生成するための変換に使用する画像となります。
Cloudflare Imagesを使用すると、画像を迅速かつ安全に保存、リサイズ、最適化、配信できます。始めるには、Cloudflareダッシュボードに画像をアップロードするか、Upload APIを使用します。
Cloudflareダッシュボードを使用して画像をアップロードするには:
- Cloudflare Dashboard ↗にログインし、アカウントを選択します。
- Imagesを選択します。
- Quick Uploadを使用して、画像をドラッグアンドドロップするか、クリックしてローカルファイルからファイルを選択します。
- 画像がアップロードされたら、生成されたURLを使用して表示します。
Upload via URL APIを使用して画像をアップロードするには、以下の例を参照してください:
curl --request POST \ --url https://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/images/v1 \ --header 'Authorization: Bearer <API_TOKEN>' \ --form 'url=<PATH_TO_IMAGE>' \ --form 'metadata={"key":"value"}' \ --form 'requireSignedURLs=false'ACCOUNT_ID: 現在のユーザーのアカウントIDは、アカウント設定で見つけることができます。API_TOKEN: Imagesの権限をスコープするために生成する必要があります。PATH_TO_IMAGE: アップロードしたい画像のURLを示します。
次に、以下のようなレスポンスを受け取ります:
{ "result": { "id": "2cdc28f0-017a-49c4-9ed7-87056c83901", "filename": "image.jpeg", "metadata": { "key": "value" }, "uploaded": "2022-01-31T16:39:28.458Z", "requireSignedURLs": false, "variants": [ "https://imagedelivery.net/Vi7wi5KSItxGFsWRG2Us6Q/2cdc28f0-017a-49c4-9ed7-87056c83901/public", "https://imagedelivery.net/Vi7wi5KSItxGFsWRG2Us6Q/2cdc28f0-017a-49c4-9ed7-87056c83901/thumbnail" ] }, "success": true, "errors": [], "messages": []}画像をアップロードしたので、これを動画のサムネイルの背景画像として使用します。
画像をアップロードした後、テキストを画像に変換するWorkerを作成します。この画像は、アップロードした背景画像のオーバーレイとして使用できます。rustwasm-worker-template ↗を使用してください。
始める前に、以下が必要です:
-
最新のRust ↗のバージョン。
-
cargo-generateサブコマンドへのアクセス:Terminal window cargo install cargo-generate
worker-rustテンプレートを使用して新しいWorkerプロジェクトを作成します:
cargo generate https://github.com/cloudflare/rustwasm-worker-templateプロジェクトディレクトリ内のファイルにいくつかの変更を加えます。
lib.rsファイルに以下のコードブロックを追加します:
use worker::*;mod utils;
#[event(fetch)]pub async fn main(req: Request, env: Env, _ctx: worker::Context) -> Result<Response> { // パニックが発生した場合にコンソールに役立つエラーメッセージを表示するオプション。 utils::set_panic_hook();
let router = Router::new(); router .get("/", |_, _| Response::ok("Hello from Workers!")) .run(req, env) .await}worker-to-textプロジェクトディレクトリ内のCargo.tomlファイルを更新して、テキストをPNGにレンダリングするためのRustパッケージであるtext-to-png ↗を使用します。以下のコマンドを実行してパッケージを依存関係として追加します:
cargo add text-to-png@0.2.0worker-to-textプロジェクトのlib.rsファイルにtext_to_pngライブラリをインポートします。
use text_to_png::{TextPng, TextRenderer};use worker::*;mod utils;
#[event(fetch)]pub async fn main(req: Request, env: Env, _ctx: worker::Context) -> Result<Response> { // パニックが発生した場合にコンソールに役立つエラーメッセージを表示するオプション。 utils::set_panic_hook();
let router = Router::new(); router .get("/", |_, _| Response::ok("Hello from Workers!")) .run(req, env) .await}lib.rsを更新して、URLのクエリパラメータとして渡されたテキストに基づいて画像変換をアクティブにするhandle-slash関数を作成します。
use text_to_png::{TextPng, TextRenderer};use worker::*;mod utils;
#[event(fetch)]pub async fn main(req: Request, env: Env, _ctx: worker::Context) -> Result<Response> { // パニックが発生した場合にコンソールに役立つエラーメッセージを表示するオプション。 utils::set_panic_hook();
let router = Router::new(); router .get("/", |_, _| Response::ok("Hello from Workers!")) .run(req, env) .await}
async fn handle_slash(text: String) -> Result<Response> {}handle-slash関数内で、カスタムフォントを使用することを指定してTextRendererをrenderer値に割り当てます。次に、render_text_to_png_dataメソッドを使用してテキストを画像形式に変換します。この例では、カスタムフォント(Inter-Bold.ttf)はプロジェクトのルートにある/assetsフォルダにあり、サムネイル生成に使用されます。この部分のコードを更新して、カスタムフォントファイルを指すようにする必要があります。
use text_to_png::{TextPng, TextRenderer};use worker::*;mod utils;
#[event(fetch)]pub async fn main(req: Request, env: Env, _ctx: worker::Context) -> Result<Response> { // パニックが発生した場合にコンソールに役立つエラーメッセージを表示するオプション。 utils::set_panic_hook();
let router = Router::new(); router .get("/", |_, _| Response::ok("Hello from Workers!")) .run(req, env) .await}
async fn handle_slash(text: String) -> Result<Response> { let renderer = TextRenderer::try_new_with_ttf_font_data(include_bytes!("../assets/Inter-Bold.ttf")) .expect("Example font is definitely loadable");
let text_png: TextPng = renderer.render_text_to_png_data(text.replace("+", " "), 60, "003682").unwrap();}Router関数を再構成して、URLにクエリが渡されたときにhandle_slashを呼び出し、そうでない場合は"Hello Worker!"をレスポンスとして返すようにします。
use text_to_png::{TextPng, TextRenderer};use worker::*;mod utils;
#[event(fetch)]pub async fn main(req: Request, env: Env, _ctx: worker::Context) -> Result<Response> { // パニックが発生した場合にコンソールに役立つエラーメッセージを表示するオプション。 utils::set_panic_hook();
let router = Router::new(); router .get_async("/", |req, _| async move { if let Some(text) = req.url()?.query() { handle_slash(text.into()).await } else { handle_slash("Hello Worker!".into()).await } }) .run(req, env) .await}
async fn handle_slash(text: String) -> Result<Response> { let renderer = TextRenderer::try_new_with_ttf_font_data(include_bytes!("../assets/Inter-Bold.ttf")) .expect("Example font is definitely loadable");
let text_png: TextPng = renderer.render_text_to_png_data(text.replace("+", " "), 60, "003682").unwrap();}lib.rsファイル内で、レスポンスがPNG画像として正しくレンダリングされるようにcontent-type: image/pngのヘッダーを設定します。
use text_to_png::{TextPng, TextRenderer};use worker::*;mod utils;
#[event(fetch)]pub async fn main(req: Request, env: Env, _ctx: worker::Context) -> Result<Response> { // パニックが発生した場合にコンソールに役立つエラーメッセージを表示するオプション。 utils::set_panic_hook();
let router = Router::new(); router .get_async("/", |req, _| async move { if let Some(text) = req.url()?.query() { handle_slash(text.into()).await } else { handle_slash("Hello Worker!".into()).await } }) .run(req, env) .await}
async fn handle_slash(text: String) -> Result<Response> { let renderer = TextRenderer::try_new_with_ttf_font_data(include_bytes!("../assets/Inter-Bold.ttf")) .expect("Example font is definitely loadable");
let text_png: TextPng = renderer.render_text_to_png_data(text.replace("+", " "), 60, "003682").unwrap();
let mut headers = Headers::new(); headers.set("content-type", "image/png")?;
Ok(Response::from_bytes(text_png.data)?.with_headers(headers))}最終的なlib.rsファイルは以下のようになります。完全なコードはGitHub ↗にある例のリポジトリで見つけることができます。
use text_to_png::{TextPng, TextRenderer};use worker::*;
mod utils;
#[event(fetch)]pub async fn main(req: Request, env: Env, _ctx: worker::Context) -> Result<Response> { // パニックが発生した場合にコンソールに役立つエラーメッセージを表示するオプション。 utils::set_panic_hook();
let router = Router::new();
router .get_async("/", |req, _| async move { if let Some(text) = req.url()?.query() { handle_slash(text.into()).await } else { handle_slash("Hello Worker!".into()).await } }) .run(req, env) .await}
async fn handle_slash(text: String) -> Result<Response> { let renderer = TextRenderer::try_new_with_ttf_font_data(include_bytes!("../assets/Inter-Bold.ttf")) .expect("Example font is definitely loadable");
let text = if text.len() > 128 { "Nope".into() } else { text };
let text = urlencoding::decode(&text).map_err(|_| worker::Error::BadEncoding)?;
let text_png: TextPng = renderer.render_text_to_png_data(text.replace("+", " "), 60, "003682").unwrap();
let mut headers = Headers::new(); headers.set("content-type", "image/png")?;
Ok(Response::from_bytes(text_png.data)?.with_headers(headers))}プロジェクトの更新が完了したら、以下のコマンドを実行してWorkerのローカルサーバーを起動します:
npx wrangler devこれにより、画像が表示されるlocalhostインスタンスが起動します:

カスタムテキストを含むクエリパラメータを追加すると、次のような画像が得られます:

Workerをデプロイするには、wrangler.tomlファイルを開き、nameキーをプロジェクト名に更新します。以下は、このチュートリアルのプロジェクト名の例です:
name = "worker-to-text"次に、npx wrangler deployコマンドを実行してWorkerをデプロイします。
npx wrangler deploywrangler deployを実行すると、Worker用の.workers.devドメインが生成されます。このドメインをメインのサムネイル画像で使用します。
アップロードした画像をImagesから提供するWorkerを作成するには、次のコマンドを実行します:
npm create cloudflare@latest -- thumbnail-imageyarn create cloudflare@latest thumbnail-imagepnpm create cloudflare@latest thumbnail-imageFor setup, select the following options:
- For What would you like to start with?, choose
Hello World example. - For Which template would you like to use?, choose
Hello World Worker. - For Which language do you want to use?, choose
JavaScript. - For Do you want to use git for version control?, choose
Yes. - For Do you want to deploy your application?, choose
No(we will be making some changes before deploying).
Workerの開発を開始するには、新しいプロジェクトディレクトリにcdします:
cd thumbnail-imageこれにより、thumbnail-imageという新しいWorkerプロジェクトが作成されます。src/index.jsファイルに以下のコードブロックを追加します:
export default { async fetch(request, env) { const url = new URL(request.url); if (url.pathname === "/original-image") { const image = await fetch( `https://imagedelivery.net/${env.CLOUDFLARE_ACCOUNT_HASH}/${IMAGE_ID}/public`, ); return image; } return new Response("Image Resizing with a Worker"); },};env.CLOUDFLARE_ACCOUNT_HASHをCloudflareアカウントIDで更新します。env.IMAGE_IDを画像IDで更新します。
Workerを実行し、/original-imageルートにアクセスして画像を確認します。
次に、Cloudflare画像変換を使用して、fetchメソッドで動的テキスト画像を背景画像の上にオーバーレイとして追加します。結果の画像を別のルートで表示することから始めます。新しいルートを/thumbnailと呼びます。
export default { async fetch(request, env) { const url = new URL(request.url); if (url.pathname === "/original-image") { const image = await fetch( `https://imagedelivery.net/${env.CLOUDFLARE_ACCOUNT_HASH}/${IMAGE_ID}/public`, ); return image; }
if (url.pathname === "/thumbnail") { }
return new Response("ワーカーによる画像リサイズ"); },};次に、fetchメソッドを使用して、背景画像の上に画像変換の変更を適用します。オーバーレイオプションはoptions.cf.imageにネストされています。
export default { async fetch(request, env) { const url = new URL(request.url);
if (url.pathname === "/original-image") { const image = await fetch( `https://imagedelivery.net/${env.CLOUDFLARE_ACCOUNT_HASH}/${IMAGE_ID}/public`, ); return image; }
if (url.pathname === "/thumbnail") { fetch(imageURL, { cf: { image: {}, }, }); }
return new Response("ワーカーによる画像リサイズ"); },};imageURLは、背景画像として使用したい画像のURLです。cf.imageオブジェクト内で、背景画像に適用したいオプションを指定します。
背景画像をGitHubのassetsディレクトリに追加し、変更をGitHubにプッシュします。画像を左クリックしてCopy Remote File Urlオプションを選択することで、画像のアップロードURLをコピーします。
imageURLの値をコピーしたリモートURLに置き換えます。
if (url.pathname === "/thumbnail") { const imageURL = "https://github.com/lauragift21/social-image-demo/blob/1ed9044463b891561b7438ecdecbdd9da48cdb03/assets/cover.png?raw=true"; fetch(imageURL, { cf: { image: {}, }, });}次に、画像オブジェクトにオーバーレイオプションを追加します。YouTubeのサムネイルに適した幅と高さに画像をリサイズし、drawオプションを使用して、デプロイされたtext-to-imageワーカーのURLを使用してオーバーレイテキストを追加します。
fetch(imageURL, { cf: { image: { width: 1280, height: 720, draw: [ { url: "https://text-to-image.examples.workers.dev", left: 40, }, ], }, },});画像変換は、ワーカーをデプロイしたときにのみテストできます。
ワーカーをデプロイするには、wrangler.tomlファイルを開き、nameキーをプロジェクト名で更新します。以下は、このチュートリアルのプロジェクト名の例です:
name = "thumbnail-image"次のコマンドを実行してワーカーをデプロイします:
npx wrangler deployこのコマンドは、ワーカーをカスタムworkers.devサブドメインにデプロイします。.workers.devサブドメインに移動し、/thumbnailルートにアクセスします。
リサイズされた画像にHello Workers!というテキストが表示されるはずです。
![]()
これで、適用されたテキストを動的にします。テキストを動的にすることで、テキストを変更し、画像に自動的に更新させることができます。
動的テキストを追加するには、クエリパラメータを使用して/thumbnail URLに付随するテキストを追加し、それをtext-to-imageワーカーのURLにパラメータとして渡します。
for (const title of url.searchParams.values()) { try { const editedImage = await fetch(imageURL, { cf: { image: { width: 1280, height: 720, draw: [ { url: `https://text-to-image.examples.workers.dev/?${title}`, left: 50, }, ], }, }, }); return editedImage; } catch (error) { console.log(error); }}これにより、生成された画像にクエリ文字列として渡したテキストが常に返されます。この例のURL、https://socialcard.cdnuptime.com/thumbnail?Getting%20Started%20With%20Cloudflare%20Images ↗は、次の画像を生成します:
![]()
このチュートリアルを完了することで、カスタムYouTubeサムネイルジェネレーターを成功裏に作成しました。
このチュートリアルでは、Cloudflare WorkersとCloudflareの画像変換を使用してカスタムYouTubeサムネイルを生成する方法を学びました。Cloudflare Workersと画像変換について詳しく学ぶには、ワーカーで画像をリサイズするを参照してください。