使用预签名URL改造hls.js视频分发方案及相关疑问
问题解答
方案可行性
完全可行。预签名URL是云存储服务的标准功能,可授权客户端在有限时间内直接访问私有存储资源,无需通过Vercel服务器中转,完全适配HLS播放场景。
能否减少Fast Origin Transfer流量
绝对可以。当前方案中,Vercel需要从存储桶拉取视频流再转发给客户端,这部分从存储桶到Vercel的流量就是Fast Origin Transfer。改成预签名URL方案后,客户端直接向存储桶发起请求,Vercel仅需处理生成预签名URL的轻量请求,不会再产生大量中转流量,能大幅降低这部分费用。
具体实现步骤
1. 后端修改:新增预签名URL生成接口
替换原有的视频转发接口,改为生成存储桶预签名URL的接口。以下以AWS S3兼容存储服务(如Cloudflare R2、AWS S3)为例:
import { GetObjectCommand, S3Client } from "@aws-sdk/client-s3"; import { getSignedUrl } from "@aws-sdk/s3-request-presigner"; export async function GET(context: APIContext): Promise<Response> { const { video_url } = context.params; if (!video_url) { return new Response(JSON.stringify({ message: "Los campos son requeridos" }), { status: 400 }); } try { // 初始化存储桶客户端(根据实际服务配置调整) const s3Client = new S3Client({ region: import.meta.env.BUCKET_REGION, credentials: { accessKeyId: import.meta.env.BUCKET_ACCESS_KEY, secretAccessKey: import.meta.env.BUCKET_SECRET_KEY, }, // Cloudflare R2需额外配置endpoint // endpoint: import.meta.env.BUCKET_ENDPOINT }); // 生成有效期1小时的预签名URL const command = new GetObjectCommand({ Bucket: import.meta.env.BUCKET_NAME, Key: video_url // 需与存储桶内文件路径一致 }); const signedUrl = await getSignedUrl(s3Client, command, { expiresIn: 3600 }); return new Response(JSON.stringify({ signedUrl }), { status: 200, headers: { 'Content-Type': 'application/json', 'Cache-Control': 'public, max-age=300' // 缓存预签名结果5分钟 } }); } catch (error) { console.error('生成预签名URL失败:', error); return new Response('Internal Server Error', { status: 500 }); } }
2. 客户端修改:先获取预签名URL再播放
调整前端逻辑,先调用预签名接口拿到URL,再传给HLS播放器或设置视频源:
async function loadVideo(videoElement, video_url) { try { // 第一步:获取预签名URL const response = await fetch(`/api/videos/getSignedUrl/${video_url}`); if (!response.ok) throw new Error('获取预签名URL失败'); const { signedUrl } = await response.json(); // 第二步:使用预签名URL播放 if (Hls.isSupported()) { const hls = new Hls(); hls.attachMedia(videoElement); hls.on(Hls.Events.MEDIA_ATTACHED, () => hls.loadSource(signedUrl)); // 可选:处理URL过期后的重试逻辑 hls.on(Hls.Events.ERROR, (event, data) => { if (data.fatal && data.type === Hls.ErrorTypes.NETWORK_ERROR) { loadVideo(videoElement, video_url); } }); } else if (videoElement.canPlayType('application/vnd.apple.mpegurl')) { videoElement.src = signedUrl; } } catch (error) { console.error('加载视频失败:', error); } } // 调用示例 const video = document.getElementById('your-video-element'); loadVideo(video, 'your-video-path.m3u8');
关键注意事项
- 预签名有效期:建议设置为1-2小时,平衡安全性与播放体验,避免过短导致播放中途URL过期。
- HLS分片处理:若存储桶为私有,且m3u8内分片使用相对路径,可选择两种方案:
- 后端获取m3u8内容,将分片路径替换为预签名URL后返回(m3u8文件体积小,流量可忽略);
- 为每个分片单独生成预签名URL(需前端额外处理,请求量较大)。
内容的提问来源于stack exchange,提问作者Bruno Munné
相关产品推荐
相关产品推荐

