You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.01 15:57:04