跳转到内容
搜索文档

使用 Workers 和 Cloudflare Image Resizing 生成 YouTube 缩略图

最后更新 查看 MarkdownAgent 设置

在本教程中,你将学习如何使用 Cloudflare Workers 和 Cloudflare Image Resizing 以编程方式生成自定义 YouTube 缩略图。你可能希望生成自定义 YouTube 缩略图,以自定义缩略图的设计、号召性用语和所用图片,鼓励更多观众观看你的视频。

本教程将帮助你了解如何使用 ImagesImage ResizingCloudflare Workers

开始之前

所有教程都假设你已经完成了快速入门指南,该指南帮助你设置 Cloudflare Workers 账户、C3Wrangler

要跟随本教程,请确保机器上已安装 Node、Cargo 和 Wrangler

学习目标

在本教程中,你将学习如何:

  • 使用 Cloudflare 仪表板或 API 将图片上传到 Cloudflare。
  • 使用 Wrangler 设置 Worker 项目。
  • 在 Worker 中使用图片变换处理图片。

上传图片

要生成自定义缩略图,首先需要将背景图片上传到 Cloudflare Images。这将作为你用于变换以生成缩略图的图片。

Cloudflare Images 允许你以快速安全的方式存储、调整大小、优化和交付图片。要开始使用,将图片上传到 Cloudflare 仪表板或使用 Upload API。

使用仪表板上传

要使用 Cloudflare 仪表板上传图片:

  1. 在 Cloudflare 仪表板中,前往 Transformations(转换) 页面。

    Go to Transformations ↗
  2. 使用 Quick Upload(快速上传) 拖放图片或点击浏览并从本地文件选择文件。

  3. 图片上传后,使用生成的 URL 查看。

使用 API 上传

要使用 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

上传图片后,创建一个 Worker,使你能够将文本转换为图片。此图片可用作你上传的背景图片上的叠加层。使用 rustwasm-worker-template

开始之前,你需要:

  • 最新版本的 Rust

  • 访问 cargo-generate 子命令:

    cargo install cargo-generate

使用 worker-rust 模板创建新的 Worker 项目:

cargo generate https://github.com/cloudflare/rustwasm-worker-template

现在需要对项目目录中的文件进行一些更改。

  1. lib.rs 文件中,添加以下代码块:
use worker::*;
mod utils;

#[event(fetch)]
pub async fn main(req: Request, env: Env, _ctx: worker::Context) -> Result<Response> {
   // Optionally, get more helpful error messages written to the console in the case of a panic.
   utils::set_panic_hook();

   let router = Router::new();
   router
       .get("/", |_, _| Response::ok("Hello from Workers!"))
       .run(req, env)
       .await
}
  1. 更新 worker-to-text 项目目录中的 Cargo.toml 文件以使用 text-to-png,这是一个用于将文本渲染为 PNG 的 Rust 包。通过运行以下命令添加依赖:
cargo add [email protected]
  1. text_to_png 库导入 worker-to-text 项目的 lib.rs 文件。
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> {
   // Optionally, get more helpful error messages written to the console in the case of a panic.
   utils::set_panic_hook();

   let router = Router::new();
   router
       .get("/", |_, _| Response::ok("Hello from Workers!"))
       .run(req, env)
       .await
}
  1. 更新 lib.rs 以创建 handle-slash 函数,该函数将根据 URL 查询参数中传递的文本激活图片变换。
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> {
   // Optionally, get more helpful error messages written to the console in the case of a panic.
   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> {}
  1. handle-slash 函数中,通过将其分配给 renderer 值来调用 TextRenderer,指定要使用自定义字体。然后,使用 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> {
   // Optionally, get more helpful error messages written to the console in the case of a panic.
   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();
}
  1. 重写 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> {
   // Optionally, get more helpful error messages written to the console in the case of a panic.
   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();
}
  1. lib.rs 文件中,将头设置为 content-type: image/png,以便响应正确渲染为 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> {
   // Optionally, get more helpful error messages written to the console in the case of a panic.
   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> {
    // Optionally, get more helpful error messages written to the console in the case of a panic.
    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 实例并显示图片:

运行 wrangler dev 以启动 Worker 的本地服务器

添加带自定义文本的查询参数,你将收到:

按照上述说明接收输出图片

要部署 Worker,打开 Wrangler 文件并使用项目名称更新 name 键。以下是本教程项目名称的示例:

{
	"$schema": "./node_modules/wrangler/config-schema.json",
	"name": "worker-to-text"
}
"$schema" = "./node_modules/wrangler/config-schema.json"
name = "worker-to-text"

然后运行 npx wrangler deploy 命令部署 Worker。

npx wrangler deploy

运行 wrangler deploy 后将为 Worker 生成 .workers.dev 域名。你将在主缩略图中使用此域名。

创建显示原始图片的 Worker

通过运行以下命令创建 Worker 以提供你上传到 Images 的图片:

npm create cloudflare@latest -- thumbnail-image

进行设置时,请选择以下选项:

  • 对于 What would you like to start with?,选择 Hello World example
  • 对于 Which template would you like to use?,选择 Worker only
  • 对于 Which language do you want to use?,选择 JavaScript
  • 对于 Do you want to use git for version control?,选择 Yes
  • 对于 Do you want to deploy your application?,选择 No(部署前我们还会做一些修改)。

要开始开发 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("Image Resizing with a Worker");
	},
};

接下来,使用 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("Image Resizing with a Worker");
	},
};

imageURL 是用作背景图片的图片 URL。在 cf.image 对象中,指定要应用于背景图片的选项。

将背景图片添加到 GitHub 上的 assets 目录并推送更改到 GitHub。通过对图片执行左键单击并选择 Copy Remote File Url(复制远程文件 URL) 选项复制图片上传的 URL。

用复制的远程 URL 替换 imageURL 值。

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 Worker URL 添加叠加文本。

fetch(imageURL, {
	cf: {
		image: {
			width: 1280,
			height: 720,
			draw: [
				{
					url: "https://text-to-image.examples.workers.dev",
					left: 40,
				},
			],
		},
	},
});

图片变换只能在部署 Worker 后进行测试。

要部署 Worker,打开 Wrangler 文件并使用项目名称更新 name 键。以下是本教程项目名称的示例:

{
	"$schema": "./node_modules/wrangler/config-schema.json",
	"name": "thumbnail-image"
}
"$schema" = "./node_modules/wrangler/config-schema.json"
name = "thumbnail-image"

通过运行以下命令部署 Worker:

npx wrangler deploy

该命令将 Worker 部署到自定义 workers.dev 子域名。前往 .workers.dev 子域名并访问 /thumbnail 路由。

你应该看到带有 Hello Workers! 文本的调整大小后的图片。

按照上述步骤生成调整大小后的图片。

现在你将使应用的文本变为动态。使文本动态化将允许你更改文本并自动在图片上更新。

要添加动态文本,将附加到 /thumbnail URL 的任何文本作为查询参数追加,并将其作为参数传递给 text-to-image Worker 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 和图片变换的信息,请参阅使用 Worker 调整图片大小

这篇文档对您有帮助吗?