Flutter使用audio_service包时音频流同步保存到磁盘的推荐方法
关于audio_service边播放流媒体边写入磁盘的实现方案
首先明确结论:可以实现。audio_service本身主要负责音频后台播放的生命周期、系统媒体控制、通知栏交互等逻辑,不直接处理音频流的拉取和解析,你可以通过搭配其官方推荐的播放内核just_audio,自定义流数据源的方式实现类似Python中stream=True的边拉边存效果,具体方案如下:
核心思路
你需要自定义音频流的中间处理层:
- 自行发起HTTP流式请求拉取音频资源
- 将请求获取到的流数据分成两路:一路写入本地磁盘做缓存,另一路传给播放内核用于播放
具体实现步骤
- 引入必要依赖
你需要在项目中添加以下依赖包:
audio_service:实现后台播放管理just_audio:实现音频解码播放,支持自定义流数据源http:实现流式HTTP请求path_provider:获取本地可写入的存储路径
- 自定义支持边存边播的音频源
继承just_audio提供的StreamAudioSource类,重写request方法,在流数据返回时同时完成写入磁盘和返回给播放器的操作,参考代码如下:
import 'dart:io'; import 'package:http/http.dart' as http; import 'package:just_audio/just_audio.dart'; import 'package:path_provider/path_provider.dart'; class CacheStreamAudioSource extends StreamAudioSource { // 音频流的远程URL final String audioUrl; // 本地保存的文件路径 final String localSavePath; CacheStreamAudioSource(this.audioUrl, this.localSavePath); @override Future<StreamAudioResponse> request([int? start, int? end]) async { // 处理范围请求,支持拖拽进度播放 final requestHeaders = <String, String>{}; if (start != null) { requestHeaders['Range'] = 'bytes=$start-${end ?? ""}'; } // 发起流式HTTP请求 final request = http.Request('GET', Uri.parse(audioUrl)); request.headers.addAll(requestHeaders); final response = await http.Client().send(request); // 初始化本地文件写入流 final saveFile = File(localSavePath); final fileWriteSink = saveFile.openWrite( mode: start == 0 ? FileMode.write : FileMode.append ); // 流数据透传同时写入本地 final processedStream = response.stream.map((dataChunk) { fileWriteSink.add(dataChunk); return dataChunk; }); // 流关闭时同步关闭文件写入流,避免资源泄漏 processedStream.listen( (_) {}, onDone: () => fileWriteSink.close(), onError: (_) => fileWriteSink.close(), ); return StreamAudioResponse( sourceLength: response.contentLength, contentLength: response.contentLength, offset: start, stream: processedStream, contentType: response.headers['content-type'] ?? 'audio/mpeg', ); } }
- 和audio_service对接使用
你不需要修改原有audio_service的业务逻辑,只需要在设置播放资源时,将原本的远程URL替换为上述自定义的CacheStreamAudioSource实例即可:
// 示例:获取本地存储路径后初始化自定义音频源 final saveDir = await getApplicationDocumentsDirectory(); final savePath = "${saveDir.path}/temp_audio.mp3"; final audioSource = CacheStreamAudioSource("你的音频URL", savePath); // 传给just_audio播放 await player.setAudioSource(audioSource); player.play();
注意事项
- 要妥善处理流的异常关闭逻辑,避免出现文件写入不完整、资源泄漏的问题
- 如果需要支持进度拖拽,需要正确处理HTTP Range请求的分片写入逻辑,避免文件内容错乱
- 对于时长较长的音频,建议实现分片缓存逻辑,避免单次写入占用过多内存
内容的提问来源于stack exchange,提问作者Survy
相关产品推荐
相关产品推荐

