能否借助supabase-js获取Supabase存储视频首帧制作缩略图?
用Supabase实现存储视频的第一帧缩略图展示
完全可以实现这个需求,下面是几种落地的方案,按需选择:
方案一:预生成缩略图(推荐,彻底节省带宽)
通过Supabase Edge Functions结合FFmpeg,在视频上传到存储桶时自动提取第一帧并保存为缩略图,后续前端直接加载缩略图即可,完全不用请求视频文件。
具体步骤:
- 创建一个Supabase Edge Function,引入FFmpeg库处理视频:
- 安装依赖:
npm install @ffmpeg/ffmpeg @ffmpeg/core - 编写函数逻辑,核心是读取上传的视频文件,执行FFmpeg命令提取第一帧:
import { serve } from "https://deno.land/std@0.168.0/http/server.ts"; import { FFmpeg } from "https://cdn.skypack.dev/@ffmpeg/ffmpeg@0.12.6"; import { fetchFile, toBlobURL } from "https://cdn.skypack.dev/@ffmpeg/util@0.12.1"; import { createClient } from "https://esm.sh/@supabase/supabase-js@2.33.1"; serve(async (req) => { const supabase = createClient( Deno.env.get("SUPABASE_URL")!, Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")! ); // 获取上传的视频信息(从存储桶webhook的请求体中) const { record } = await req.json(); const videoPath = record.path; // 下载视频到Edge Function临时存储 const { data: videoData, error: downloadError } = await supabase.storage .from("videos") .download(videoPath); if (downloadError) return new Response(JSON.stringify(downloadError), { status: 500 }); // 初始化FFmpeg const ffmpeg = new FFmpeg(); await ffmpeg.load({ coreURL: await toBlobURL( "https://unpkg.com/@ffmpeg/core@0.12.6/dist/ffmpeg-core.js", "text/javascript" ), }); // 写入视频文件 await ffmpeg.writeFile("input.mp4", await fetchFile(videoData)); // 执行提取第一帧的命令 await ffmpeg.exec(["-i", "input.mp4", "-vframes", "1", "-f", "image2", "output.jpg"]); // 读取生成的缩略图 const thumbnailData = await ffmpeg.readFile("output.jpg"); // 上传缩略图到存储桶(比如单独的thumbnails桶) const thumbnailPath = `thumbnails/${videoPath.split("/").pop()!.replace(".mp4", ".jpg")}`; const { error: uploadError } = await supabase.storage .from("videos") .upload(thumbnailPath, new Blob([thumbnailData], { type: "image/jpeg" }), { contentType: "image/jpeg", }); if (uploadError) return new Response(JSON.stringify(uploadError), { status: 500 }); // 更新数据库中对应视频的缩略图路径(如果有对应表的话) const { error: dbError } = await supabase .from("videos") .update({ thumbnail_path: thumbnailPath }) .eq("video_path", videoPath); if (dbError) return new Response(JSON.stringify(dbError), { status: 500 }); return new Response("Thumbnail generated successfully", { status: 200 }); });
- 安装依赖:
- 给存储桶配置webhook,当有新视频上传时触发这个Edge Function:
- 进入Supabase控制台的存储页面,选中视频存储桶,在"Webhooks"标签下添加新webhook,触发事件选择"Object created",目标URL填Edge Function的部署地址。
方案二:前端按需生成缩略图(快速实现,适合小量视频)
如果视频数量不多,可以直接在前端通过视频URL提取第一帧,无需后端预生成。这种方式浏览器只会加载视频的元数据和第一帧数据,带宽消耗远低于加载整个视频。
代码示例:
async function generateVideoThumbnail(videoUrl) { const video = document.createElement('video'); // 处理跨域问题(如果视频存储桶配置了正确的CORS) video.crossOrigin = 'anonymous'; video.src = videoUrl; // 等待视频加载到第一帧 await new Promise((resolve, reject) => { video.onloadeddata = resolve; video.onerror = reject; }); // 用Canvas绘制第一帧 const canvas = document.createElement('canvas'); canvas.width = video.videoWidth; canvas.height = video.videoHeight; const ctx = canvas.getContext('2d'); ctx.drawImage(video, 0, 0); // 转换成Base64格式的图片 return canvas.toDataURL('image/jpeg', 0.8); } // 使用示例 const videoUrl = 'https://your-supabase-url.storage.googleapis.com/videos/example.mp4'; generateVideoThumbnail(videoUrl).then(thumbnailUrl => { const img = document.createElement('img'); img.src = thumbnailUrl; document.body.appendChild(img); });
方案三:数据库关联缩略图路径
不管用哪种方式生成缩略图,建议在Supabase数据库中创建对应表(比如videos),存储视频路径和缩略图路径的关联关系。前端直接从数据库读取缩略图URL,避免重复生成或处理,提升加载效率。
示例表结构:
CREATE TABLE videos ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), video_path TEXT NOT NULL, thumbnail_path TEXT, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() );
内容的提问来源于stack exchange,提问作者Rorp
相关产品推荐
相关产品推荐

