如何实现Supabase存储中多Blob音频的选择与播放?
问题分析与修复方案
你的代码存在几个核心问题导致音频无法播放:
urls是普通变量而非React状态,组件重渲染时会丢失数据,且获取Blob URL后无法触发组件更新,导致audio标签拿不到有效src- 每个
GeneratedClones组件都重复拉取所有音频文件,存在冗余请求 - 点击事件未传递当前文件的正确标识,且依赖异步状态
playAudio判断播放状态会有延迟 - props传递的
id是文件ID,不是文件名,urls[id.name]属于错误的取值方式
修复步骤
1. 父组件修改:传递文件名而非ID
在渲染GeneratedClones时,直接传递文件的完整名称(Supabase下载需要文件名):
{/* Displaying already generated audios */} <div id="generated" className="pl-[10%] pt-[7.5%]"> <Card id="generated-card" className="w-[450px] h-[73.5%]"> <CardHeader> <CardTitle>Generated clones</CardTitle> <CardDescription>Here you can see your already generated clones.</CardDescription> </CardHeader> <CardContent className="space-y-3"> {audioFiles.length > 0 ? ( audioFiles.map((file) => { if (file.name !== '.emptyFolderPlaceholder') { const nameOnly = file.name.split("-")[0]; return ( <GeneratedClones key={file.name} name={nameOnly} timestamp={file.updated_at} metadata={file.metadata} fileName={file.name} {/* 传递完整文件名 */} /> ) } }) ) : <p>you currently have no generated clones :(</p> } </CardContent> </Card> </div>
2. GeneratedClones组件重构
const GeneratedClones = ({ name, timestamp, fileName }) => { const { user } = useUser(); const audioRef = useRef(); const [isPlaying, setIsPlaying] = useState(false); const [audioUrl, setAudioUrl] = useState(null); // 用状态存储当前音频的Blob URL // 仅获取当前组件对应的音频文件 const fetchAudioUrl = async () => { if (!user || !fileName) return; const userEmail = user.email; try { const { data: file, error } = await supabase .storage .from("audios") .download(`${userEmail}/${fileName}`); if (error) throw error; const url = URL.createObjectURL(file); setAudioUrl(url); } catch (err) { console.error("下载音频失败:", err); } }; useEffect(() => { fetchAudioUrl(); // 组件卸载时释放Blob URL,避免内存泄漏 return () => { if (audioUrl) { URL.revokeObjectURL(audioUrl); } }; }, [user, fileName]); const handlePlayClick = () => { if (!audioRef.current) return; // 直接操作audio元素,避免状态异步更新的延迟问题 if (isPlaying) { audioRef.current.pause(); } else { audioRef.current.play(); } setIsPlaying(prev => !prev); }; return ( <div> <Card className="pt-2"> <CardContent> <div className="flex items-center"> <div id="generated-svg" className="pr-4" onClick={handlePlayClick}> {isPlaying ? ( <svg fill="#000000" height="65px" width="65px" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" stroke="#000000"><g id="SVGRepo_bgCarrier" strokeWidth="0"></g><g id="SVGRepo_tracerCarrier" strokeLinecap="round" strokeLinejoin="round"></g><g id="SVGRepo_iconCarrier"> <path d="M256,0C114.617,0,0,114.615,0,256s114.617,256,256,256s256-114.615,256-256S397.383,0,256,0z M224,320 c0,8.836-7.164,16-16,16h-32c-8.836,0-16-7.164-16-16V192c0-8.836,7.164-16,16-16h32c8.836,0,16,7.164,16,16V320z M352,320 c0,8.836-7.164,16-16,16h-32c-8.836,0-16-7.164-16-16V192c0-8.836,7.164-16,16-16h32c8.836,0,16,7.164,16,16V320z"></path> </g></svg> ) : ( <svg width={65} height={65} fill="#000000" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 408.221 408.221" stroke="#000000"><g id="SVGRepo_bgCarrier" strokeWidth="0"></g><g id="SVGRepo_tracerCarrier" strokeLinecap="round" strokeLinejoin="round"></g><g id="SVGRepo_iconCarrier"> <g> <g> <path d="M204.11,0C91.388,0,0,91.388,0,204.111c0,112.725,91.388,204.11,204.11,204.11c112.729,0,204.11-91.385,204.11-204.11 C408.221,91.388,316.839,0,204.11,0z M286.547,229.971l-126.368,72.471c-17.003,9.75-30.781,1.763-30.781-17.834V140.012 c0-19.602,13.777-27.575,30.781-17.827l126.368,72.466C303.551,204.403,303.551,220.217,286.547,229.971z"></path> </g> </g> </g></svg> )} </div> <div id="generated-content"> <h2 id="name" className="font-semibold text-2xl">{name}</h2> <p className="text-muted-foreground">Created at {timestamp}</p> </div> </div> </CardContent> </Card> <audio ref={audioRef} src={audioUrl} preload="metadata" {/* 优化加载体验 */} /> </div> ) }; export default GeneratedClones;
关键优化点
- 用
audioUrl状态存储Blob URL,确保组件能感知到URL变化并更新audio标签 - 每个组件仅请求自身对应的音频文件,减少冗余请求
- 点击事件直接操作audio元素,避免异步状态更新的延迟问题
- 组件卸载时释放Blob URL,防止内存泄漏
- 添加
preload="metadata"属性,提前加载音频元数据提升播放响应速度
内容的提问来源于stack exchange,提问作者anon
相关产品推荐
相关产品推荐

