Stream 与媒体
视频/Image 处理与分发。
Stream 与媒体
这一课你将能做到
- 使用 Cloudflare Stream 上传、转码、分发视频,并通过签名 URL 控制访问
- 使用 Cloudflare Images 做图片缩放、格式转换(WebP/AVIF)、懒加载
- 理解视频流媒体协议(HLS/DASH)和边缘缓存策略
为什么要学
你的 Next.js 站点要加视频教程。如果直接把 MP4 扔到服务器上,用户播放时会遇到:加载慢、卡顿、不同设备兼容性问题、流量费爆炸。如果上传到 YouTube/Bilibili,又有广告、品牌不统一、无法自定义播放器。
人话:Cloudflare Stream 就是「白标的 YouTube」。你上传视频,它自动转码成多种码率、生成 HLS/DASH 流、全球 CDN 分发、提供可定制的播放器。Images 则是「自动图片 CDN」:上传原图,它自动生成多种尺寸和格式,按设备返回最优版本。
先建立图像
视频处理流程:
[上传 MP4] ──> [Stream 转码集群]
│
┌───────────┼───────────┐
▼ ▼ ▼
[1080p] [720p] [480p]
│ │ │
└───────────┴───────────┘
│
[HLS/DASH 打包]
│
[全球 CDN 分发]
│
┌───────────┼───────────┐
▼ ▼ ▼
[北京用户] [纽约用户] [伦敦用户]
(就近节点) (就近节点) (就近节点)
图片处理流程:
[原图] ──> [Images]
│
┌─────────┼─────────┐
▼ ▼ ▼
[WebP] [AVIF] [JPEG]
[100px] [500px] [2000px]
│ │ │
└─────────┴─────────┘
│
[边缘缓存 + 按需生成]
跟着做
步骤 1:上传视频到 Stream
# 直接上传(小文件)
curl -X POST \
-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
-F "file=@/path/to/video.mp4" \
https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/stream
响应:
{
"result": {
"uid": "xxxxxxxxxxxxxxxx",
"playback": {
"hls": "https://customer-xxx.cloudflarestream.com/xxxxxxxx/manifest/video.m3u8",
"dash": "https://customer-xxx.cloudflarestream.com/xxxxxxxx/manifest/video.mpd"
},
"status": { "state": "ready" }
}
}
步骤 2:在 Worker 中生成签名 URL(防盗链)
export interface Env {
STREAM_SIGNING_KEY: string;
ACCOUNT_ID: string;
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const url = new URL(request.url);
const videoUid = url.searchParams.get('uid');
if (!videoUid) return new Response('Missing uid', { status: 400 });
const expires = Math.floor(Date.now() / 1000) + 3600; // 1 小时后过期
const token = await signToken(videoUid, expires, env.STREAM_SIGNING_KEY);
const signedUrl = `https://customer-${env.ACCOUNT_ID}.cloudflarestream.com/${videoUid}/manifest/video.m3u8?token=${token}`;
return Response.json({ url: signedUrl, expires });
},
} satisfies ExportedHandler<Env>;
async function signToken(uid: string, expires: number, key: string): Promise<string> {
const encoder = new TextEncoder();
const data = encoder.encode(`${uid}.${expires}`);
const keyData = encoder.encode(key);
const cryptoKey = await crypto.subtle.importKey(
'raw', keyData, { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']
);
const signature = await crypto.subtle.sign('HMAC', cryptoKey, data);
return btoa(String.fromCharCode(...new Uint8Array(signature)));
}