Flutter使用flutter_sound实现音频录制保存播放及找不到文件报错问询
Flutter Sound 音频录制找不到文件报错解决方案
报错根因
你遇到的Cannot open file, path = '/storage/emulated/0/myAudio.aac' (OS Error: No such file or directory, errno = 2)报错由两个核心问题导致:
- 录音时仅传入文件名未指定完整路径,flutter_sound默认将文件写入应用私有沙箱目录,无法从公共存储根目录直接读取
- 硬编码公共存储路径不符合Android高版本权限规则,且缺少必要的存储权限申请、目录合法性校验
分步解决方案
1. 补全权限配置
Android端配置(android/app/src/main/AndroidManifest.xml)
在manifest节点下添加权限:
<!-- 麦克风权限 --> <uses-permission android:name="android.permission.RECORD_AUDIO" /> <!-- Android 12及以下存储权限 --> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32" /> <!-- Android 13及以上音频媒体权限 --> <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
在application节点添加兼容配置:
<application android:requestLegacyExternalStorage="true" ...其余原有配置保持不变 >
权限申请逻辑补充
在init方法中除了麦克风权限,额外申请存储权限:
// 同时申请麦克风和存储权限 Map<Permission, PermissionStatus> statuses = await [ Permission.microphone, if (Platform.isAndroid) Platform.version >= 13 ? Permission.readMediaAudio : Permission.storage, ].request(); if (statuses[Permission.microphone] != PermissionStatus.granted) { throw RecordingPermissionException('Recording permission required.'); }
2. 修正路径与录制逻辑
不要硬编码存储路径,使用path_provider获取合法可读写目录,录制时传入完整路径,修正后的Recorder类代码如下:
import 'dart:io'; import 'dart:typed_data'; import 'package:flutter/foundation.dart'; import 'package:flutter_sound/flutter_sound.dart'; import 'package:path_provider/path_provider.dart'; import 'package:permission_handler/permission_handler.dart'; class Recorder { FlutterSoundRecorder? _recorder; FlutterSoundPlayer? _player; bool _isRecorderInitialized = false; bool _isPlayerInitialized = false; String? _audioFilePath; bool get isRecording => _recorder?.isRecording ?? false; bool get isPlaying => _player?.isPlaying ?? false; // 初始化录音器 Future initRecorder() async { _recorder = FlutterSoundRecorder(); // 申请权限 Map<Permission, PermissionStatus> statuses = await [ Permission.microphone, if (Platform.isAndroid) Platform.operatingSystemVersion.contains('13') ? Permission.readMediaAudio : Permission.storage, ].request(); if (statuses[Permission.microphone] != PermissionStatus.granted) { throw RecordingPermissionException('需要麦克风权限才能录制音频'); } // 获取合法存储路径 Directory appDocDir = await getApplicationDocumentsDirectory(); _audioFilePath = '${appDocDir.path}/myAudio.aac'; // 打开录音会话 await _recorder!.openAudioSession(); _isRecorderInitialized = true; } // 初始化播放器 Future initPlayer() async { _player = FlutterSoundPlayer(); await _player!.openAudioSession(); _isPlayerInitialized = true; } // 开始录制 Future record() async { if (!_isRecorderInitialized || _audioFilePath == null) return; if (kDebugMode) print('开始录制...'); await _recorder!.startRecorder( toFile: _audioFilePath, codec: Codec.aacADTS, ); } // 停止录制 Future stop() async { if (!_isRecorderInitialized) return; await _recorder!.stopRecorder(); if (kDebugMode) print('录制结束,文件路径:$_audioFilePath'); } // 切换录制状态 Future toggleRecording() async { if (_recorder!.isStopped) { await record(); } else { await stop(); } } // 播放录制的音频 Future play() async { if (!_isPlayerInitialized || _audioFilePath == null) return; // 先判断文件是否存在 File audioFile = File(_audioFilePath!); if (!await audioFile.exists()) { if (kDebugMode) print('音频文件不存在'); return; } await _player!.startPlayer(fromURI: _audioFilePath); } // 停止播放 Future stopPlay() async { if (!_isPlayerInitialized) return; await _player!.stopPlayer(); } // 资源释放 void dispose() { if (_isRecorderInitialized) { _recorder!.closeAudioSession(); _recorder = null; _isRecorderInitialized = false; } if (_isPlayerInitialized) { _player!.closeAudioSession(); _player = null; _isPlayerInitialized = false; } } }
3. 额外注意事项
- 如果需要将音频文件保存到公共存储目录供其他应用读取,需要使用
MediaStoreAPI写入,不要直接操作公共存储根目录,Android 10及以上版本禁止直接修改公共目录文件 - 使用完录音器和播放器后必须调用
dispose方法释放资源,避免出现会话占用异常
内容的提问来源于stack exchange,提问作者who-aditya-nawandar
相关产品推荐
相关产品推荐

