SvelteKit通过HTTP响应播放音频文件报错排查与解决
问题
使用AWS Polly文本转语音API,尝试将音频响应返回给客户端播放时,持续遇到以下错误:
GET blob:http://localhost:5173/8788f478-32ef-4e76-80a1-93c4f1a6a3a8 net::ERR_REQUEST_RANGE_NOT_SATISFIABLE localhost/:1 Uncaught (in promise) DOMException: Failed to load because no supported source was found.
生成的音频写入磁盘可以正常播放,但通过Response返回给客户端就无法播放,相关代码如下:
客户端代码(+page.svelte)
<script lang="ts"> let audio: any; const handleClick = async () => { const what = await fetch('/api/speech', { method: 'POST', headers: { 'Content-Type': 'audio/mpeg' } }); /** * Play audio from blob */ const blob = await what.blob(); const url = URL.createObjectURL(blob); audio.src = url; audio.play(); }; </script> <button on:click={handleClick}> Clicky </button> <audio bind:this={audio}> <source class="track" src="" type="audio/mpeg" /> </audio>
服务端代码(routes/api/speech/+server.ts)
import AWS from 'aws-sdk'; export const POST = async () => { const awsConfig = new AWS.Config(...); const polly = new AWS.Polly(awsConfig); const input = { Engine: 'standard', LanguageCode: 'en-US', OutputFormat: 'mp3', TextType: 'text', VoiceId: 'Ivy', Text: `hello hello` }; const speech = (await polly.synthesizeSpeech(input, (err, data: any) => { if (err) { new Response(String('err')); } /** * Return data in a way that's consumable by the browser */ if (data) { if (data.AudioStream instanceof Buffer) { // fs.writeFile('speech.mp3', data.AudioStream, function (err) { // if (err) { // return console.log(err); // } // console.log('The file was saved!'); // }); return data; } } })) as any; return new Response(speech.AudioStream, { headers: { 'Content-Type': 'audio/mpeg' } }); };
解决方法
问题集中在三个关键点,逐一修复即可解决:
1. 错误混用AWS SDK的回调与Promise模式
polly.synthesizeSpeech使用await时,不需要传入回调函数——回调内的返回值不会被await捕获,你拿到的speech其实是SDK的请求对象,而非实际音频数据。
修改服务端调用逻辑,改用纯Promise模式:
// 移除回调,直接通过promise()获取结果 const speech = await polly.synthesizeSpeech(input).promise();
2. 客户端请求头设置错误
你在POST请求中设置了Content-Type: audio/mpeg,但实际并未发送任何音频数据,这个头是多余的,还可能导致服务端误解请求格式。直接移除该请求头即可。
修复客户端fetch请求:
const what = await fetch('/api/speech', { method: 'POST' // 移除错误的Content-Type头 });
3. 响应缺少Content-Length头
浏览器播放音频时需要知晓文件大小,否则会触发范围请求(Range)错误。返回Response时,添加Content-Length头,值为音频Buffer的长度:
修复服务端Response部分:
return new Response(speech.AudioStream, { headers: { 'Content-Type': 'audio/mpeg', 'Content-Length': speech.AudioStream.length.toString() } });
额外优化:清理Blob URL
播放完成后释放Blob URL,避免内存泄漏:
audio.addEventListener('ended', () => { URL.revokeObjectURL(audio.src); });
内容的提问来源于stack exchange,提问作者Kyle Truong
相关产品推荐
相关产品推荐

