Expo将base64音频写入本地存储后用expo-av播放报错如何解决
问题核心原因
报错None of the available extractors could read the stream是因为写入本地的音频文件格式损坏,核心诱因是你调用writeAsStringAsync时传入了带data:audio/m4a;base64,前缀的内容,而FileSystem.EncodingType.Base64模式会直接对传入的整个字符串做Base64解码,前缀不属于音频的原始base64编码部分,解码后生成的文件头不符合m4a格式规范,导致解码器无法识别。你日志中显示写入的文件大小仅17字节,也可以直接证明文件内容异常。
具体解决方案
- 修正写入文件的逻辑
去掉写文件时额外添加的data URI前缀,仅传入后端返回的纯base64内容即可:
// 错误写法 await FileSystem.writeAsStringAsync(uri, `data:audio/m4a;base64,${base64}`, { encoding: FileSystem.EncodingType.Base64 }); // 正确写法 await FileSystem.writeAsStringAsync(uri, base64, { encoding: FileSystem.EncodingType.Base64 });
- 调整音频加载播放的时序逻辑
你当前代码在调用playAsync后立刻执行了unloadAsync,会导致音频还没开始播放就被卸载,需要调整为等待播放完成后再卸载资源:
try { await sound.loadAsync({ uri: uri }); console.log('音频加载完成'); // 监听播放完成事件,播放结束后再卸载资源 sound.setOnPlaybackStatusUpdate(async (status) => { if (status.didJustFinish) { await sound.unloadAsync(); } }); await sound.playAsync(); } catch(error){ console.log('播放出错', error); // 出错时也要卸载资源避免内存泄漏 await sound.unloadAsync(); }
- 可选优化:跳过本地文件写入直接播放
如果不需要长期保存音频,完全可以不用做本地IO操作,直接把完整的data URI传给loadAsync即可播放,减少IO操作带来的出错概率:
async function playSound() { let sound = new Audio.Sound(); const dataUri = `data:audio/m4a;base64,${base64}`; try { await sound.loadAsync({ uri: dataUri }); sound.setOnPlaybackStatusUpdate(async (status) => { if (status.didJustFinish) await sound.unloadAsync(); }); await sound.playAsync(); } catch (e) { console.log(e); await sound.unloadAsync(); } }
- 权限校验
确认你已经在app.json中配置了安卓的音频相关权限,并且在应用启动时申请了对应权限:
{ "expo": { "android": { "permissions": ["READ_EXTERNAL_STORAGE", "WRITE_EXTERNAL_STORAGE", "RECORD_AUDIO"] } } }
如果你使用的Expo SDK 39版本过旧,以上操作完成后仍有问题,可以尝试升级expo-av到对应SDK的最新兼容版本。
内容的提问来源于stack exchange,提问作者Guillaume D
相关产品推荐
相关产品推荐

