大家好,我是程序员柏风,AI 最全的 Vibecoding 学习地图 http://coderoadmap.cn 作者。
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)));
}
步骤 3:前端嵌入播放器
<stream src="xxxxxxxxxxxxxxxx" controls></stream>
<script data-cfasync="false" defer src="https://embed.videodelivery.net/embed/r4xu.fla9.latest.js"></script>
步骤 4:使用 Images 做响应式图片
上传图片到 Images(Dashboard 或 API),然后在 HTML 中使用:
<img
src="https://imagedelivery.net/{account_hash}/{image_id}/public"
srcset="
https://imagedelivery.net/{account_hash}/{image_id}/w=400 400w,
https://imagedelivery.net/{account_hash}/{image_id}/w=800 800w,
https://imagedelivery.net/{account_hash}/{image_id}/w=1200 1200w
"
sizes="(max-width: 600px) 400px, (max-width: 1200px) 800px, 1200px"
alt="示例图片"
/>
Images 会自动根据设备 DPR 和屏幕宽度返回最合适的尺寸和格式。
步骤 5:Worker 动态生成图片变体
export default {
async fetch(request: Request): Promise<Response> {
const url = new URL(request.url);
const imageUrl = url.searchParams.get('url');
const width = url.searchParams.get('w') ?? '800';
// 通过 Cloudflare Image Resizing 动态缩放
const resized = `https://your-domain.com/cdn-cgi/image/width=${width},format=auto/${imageUrl}`;
return fetch(resized, {
cf: { image: { width: parseInt(width), format: 'auto' } }
});
},
} satisfies ExportedHandler<Env>;
讲清楚要点
- Stream 自动转码:上传一个 MP4,Stream 自动生成 1080p/720p/480p/360p 多种码率,打包成 HLS 和 DASH。你不需要懂 FFmpeg。
- HLS 和 DASH:HLS 是 Apple 的协议,iOS/Safari 原生支持;DASH 是国际标准,Android/Chrome 支持更好。Stream 同时生成两者,播放器自动选择。
- 签名 URL 防盗链:默认视频 URL 是公开的。用签名 URL 可以控制「谁能在什么时间范围内播放」,适合付费内容。
- Images 按需生成:你不需要提前生成 10 种尺寸。Images 会根据 URL 参数(
w=400)实时生成并缓存,边缘节点直接返回。 - 格式自动协商:
format=auto会根据浏览器支持自动返回 WebP 或 AVIF。Chrome 给 WebP,Safari 给 JPEG,无需人工干预。
你可能会懵的地方
-
Stream 和直接放 R2 有什么区别? R2 是原始文件存储,没有转码、没有自适应码率、没有播放器。Stream 是完整的视频 SaaS:转码、CDN、播放器、分析、DRM(付费)。自己用 R2 + FFmpeg + HLS.js 也能做,但工程量巨大。
-
Images 和 Image Resizing 有什么区别? Images 是存储 + 处理的一体化服务;Image Resizing 是只处理(源图可以在任何地方)。Images 适合「图片不多但要求体验好」的场景;Image Resizing 适合「已有图片源,只需要加速」的场景。
-
Stream 支持直播吗? 支持。Stream Live 可以接收 RTMP 推流,自动转码分发。适合活动直播、在线课程。
-
视频审核怎么做? Stream 有内置的 NSFW 检测(付费)。也可以在上传后触发 Webhook,用 Workers AI 做自定义审核。
-
免费额度够吗? Stream 按存储分钟数和观看分钟数计费,没有免费额度。Images 有免费额度(每月 10 万次请求)。个人项目用 Images 足够,Stream 建议先小范围测试。
错法对照
| 错法 | 问题 | 正解 |
|---|---|---|
| 直接把 MP4 放 R2 让用户下载 | 没有自适应码率,大文件加载慢 | 用 Stream 转码 + HLS 分发 |
| 不设置签名 URL | 视频被恶意盗链,流量费爆炸 | 用签名 URL + 过期时间 |
| 图片只提供一种尺寸 | 手机加载 4K 图,浪费带宽 | 用 Images 的 srcset 响应式 |
用 <img src="original.jpg"> | 原图 5MB,首屏加载 10 秒 | 用 Images 的 w=800,format=auto |
| 直播用普通 MP4 推流 | 延迟高、无法互动 | 用 Stream Live 的 RTMP/WebRTC |
卡点急救
- 视频上传后一直
processing:大文件转码需要时间(几分钟到几十分钟)。检查 Dashboard 的转码进度;如果卡住,重新上传。 - 签名 URL 403:检查签名算法是否正确;检查
expires是否过期;检查uid是否匹配。 - Images 返回原图而不是 WebP:确认 URL 中有
format=auto;确认浏览器支持 WebP(Chrome/Edge/Firefox 支持,Safari 14+ 支持)。
面试常问
-
HLS 和 DASH 的核心区别? HLS 是 Apple 的协议,基于 TS 分片,iOS/Safari 原生支持;DASH 是 MPEG 国际标准,基于 fMP4 分片,Android/Chrome 支持更好。现代播放器(Video.js、Shaka Player)通常两者都支持,自动降级。
-
自适应码率(ABR)的原理? 播放器根据当前网络带宽、设备性能、缓冲区水位,动态选择最合适的码率。Stream 会自动生成多码率清单(M3U8/MPD),播放器内置 ABR 算法。
-
Cloudflare Images 和 Cloudinary 怎么比? Images 深度集成 Cloudflare CDN,边缘处理延迟低;Cloudinary 功能更全(AI 抠图、视频处理、数字资产管理)。如果已经在 Cloudflare 生态,Images 更自然;如果需要复杂图片编辑,Cloudinary 更强。
自测三问
- 为什么视频流媒体要用 HLS/DASH 而不是直接播放 MP4?
- 签名 URL 的签名算法是什么?怎么防止伪造?
- Images 的
format=auto是怎么工作的?浏览器和服务器各做了什么?
小结一句话
Cloudflare Stream 和 Images 把视频转码、自适应码率、图片响应式、格式协商这些复杂工程变成了「上传即用」的 SaaS,配合 Worker 签名 URL 可以构建安全、高性能的媒体分发系统。
延伸
- Stream 官方文档
- Images 官方文档
- 下一课:安全与防护,学习 WAF、TLS、Bot 管理与边缘安全模型。