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

Ionic 3从可暂停恢复的录制文件获取Base64音频字符串异常问题

兄弟,我之前在Android上用cordova-plugin-media做录音功能时,也踩过几乎一模一样的坑!结合我当时排查的经验,给你理清楚问题出在哪,以及怎么解决:

问题根源拆解

关于3GP格式无回调的问题

cordova-plugin-media在Android上录制3GP时,暂停再恢复后,文件的内部索引指针会出现异常状态,导致cordova-plugin-file调用readAsDataURL时,底层卡在文件读取的逻辑里,既不触发成功回调也不触发错误回调。本质是Android原生MediaRecorder在暂停恢复后,文件写入的状态没有被插件正确处理,留下了隐蔽的bug。

关于MP3/WAV只保留第一部分的问题

这俩格式本身就不支持暂停-恢复式的分段录制追加。Android原生MediaRecorder对MP3/WAV格式,暂停后重新启动录制时,会直接覆盖之前的录制内容或者重新创建文件,而cordova-plugin-media并没有做分段录制内容的合并处理,所以最终只能拿到第一次录制的那段音频。

可行解决方案

方案1:自定义分段录制+原生合并(推荐)

既然插件本身的暂停恢复逻辑不靠谱,我们可以手动实现分段录制,最后合并成完整音频:

  1. 每次点击“暂停”时,停止当前录制并保存临时音频文件;
  2. 点击“恢复”时,创建新的录制实例生成新的临时文件;
  3. 录制完成后,用Android原生的MediaMuxer工具合并所有临时文件;
  4. 最后读取合并后的文件转为Base64用于播放。

给你个Ionic/Angular的代码示例参考:

import { Media, MediaObject } from '@ionic-native/media/ngx';
import { File } from '@ionic-native/file/ngx';

// 存储临时录制文件的路径
private tempAudioFiles: string[] = [];
private currentRecorder: MediaObject | null = null;

// 启动/恢复录制
startOrResumeRecording() {
  // 生成唯一的临时文件名
  const tempFileName = `temp_rec_${Date.now()}.3gp`;
  const savePath = this.file.externalDataDirectory + tempFileName;

  // 创建新的录制实例
  this.currentRecorder = this.media.create(savePath);
  this.currentRecorder.startRecord();

  // 单个片段录制完成(暂停时触发stopRecord会走这里)
  this.currentRecorder.onSuccess.subscribe(() => {
    this.tempAudioFiles.push(savePath);
    this.currentRecorder = null;
  });

  this.currentRecorder.onError.subscribe(err => {
    console.error('单段录制失败:', err);
    this.currentRecorder = null;
  });
}

// 暂停录制
pauseRecording() {
  if (this.currentRecorder) {
    // 停止当前片段录制,触发onSuccess保存临时文件
    this.currentRecorder.stopRecord();
  }
}

// 完成录制并合并
async finishRecording() {
  // 确保最后一段录制被保存
  this.pauseRecording();

  // 这里需要自己写一个Cordova自定义插件,封装Android的MediaMuxer合并逻辑
  // 假设我们有个插件叫AudioMerger,提供mergeAudioFiles方法
  const finalFilePath = await this.audioMerger.mergeAudioFiles(
    this.tempAudioFiles,
    this.file.externalDataDirectory + 'final_voice.3gp'
  );

  // 读取合并后的文件为Base64
  this.file.readAsDataURL(this.file.externalDataDirectory, 'final_voice.3gp')
    .then(base64Audio => {
      // 用HTML audio标签播放
      const audioEl = document.createElement('audio');
      audioEl.src = base64Audio;
      audioEl.play();

      // 清理临时文件
      this.tempAudioFiles.forEach(file => this.file.removeFile(file));
      this.tempAudioFiles = [];
    })
    .catch(readErr => console.error('读取音频文件失败:', readErr));
}

方案2:规避暂停恢复逻辑(简单场景适用)

如果你的业务场景可以接受“分段手动录制”的体验(比如用户点击多次“录制”按钮,每次录一段,最后合并),那可以直接放弃插件的暂停功能,改用多次录制后合并的方式,完全避开插件的bug。

方案3:切换到Capacitor的音频插件

如果项目允许迁移到Capacitor,推荐用@capacitor-community/audio-recorder插件,它对Android的暂停恢复录制支持更完善,底层处理了原生MediaRecorder的状态问题,还能直接获取录制文件的Base64,省去cordova-plugin-file的读取步骤,省心很多。

额外注意点
  • 记得在AndroidManifest中申请RECORD_AUDIO和WRITE_EXTERNAL_STORAGE权限,并且在Ionic中正确处理权限请求;
  • 尽量用真实设备测试,模拟器的音频录制兼容性问题很多;
  • 优先用3GP格式,它是Android原生支持最好的格式,文件体积也更小,合并起来更稳定。

内容的提问来源于stack exchange,提问作者Jack

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 09:53:52