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

JS调用MediaRecorder录制audio/webm音频下载得到空Blob问题

问题根因

你遇到的两个异常本质是同一个问题导致的:

  • MediaRecorder.stop() 是异步API,调用该方法后不会同步把录制完成的音频分片全部写入recordedBlobs数组。所有录制数据都是通过ondataavailable事件回调异步推送的,你在Stop()函数里刚调用完mediaRecorder.stop()就立刻执行downloadVideo(),此时数据回调还没触发,recordedBlobs本来就是空数组,所以生成的Blob是空的、打印length结果为0。
  • 控制台里点开recordedBlobs日志能看到有内容,是浏览器控制台的懒解析机制导致的:打印引用类型对象时,控制台不会立刻固化对象的快照,等你手动点开日志查看时,异步的ondataavailable回调已经执行完成,把数据push进了数组,所以你能看到数组里的内容,但这并不代表执行console.log的瞬间数组里有数据。
修复方案

不要在调用stop()后立刻执行下载,改为监听MediaRecorder的stop事件,等所有数据分片都接收完成、录制状态完全停止后再触发下载逻辑。同时补全初始化状态判断,避免用户在麦克风权限申请完成前点击按钮报错。

修正后的完整代码如下:

<script>
    let mediaRecorder;
    let recordedBlobs;
    // 标记录音器初始化状态,避免未初始化就点击按钮
    let isRecorderReady = false;

    if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
      console.log("getUserMedia supported.");
      navigator.mediaDevices
        .getUserMedia({audio: true, video: false})
        .then(function (stream) {
           mediaRecorder = new MediaRecorder(stream);
           // 统一绑定事件回调
           mediaRecorder.ondataavailable = handleDataAvailable;
           mediaRecorder.onstop = () => {
             // 录制完全停止、所有数据接收完成后再触发下载
             downloadVideo();
           }
           isRecorderReady = true;
        })
        .catch(function (err) {
          console.log("The following getUserMedia error occurred: " + err);
        });
    } else {
      console.log("getUserMedia not supported on your browser!");
    }
    
    function handleDataAvailable(event) {
      console.log('handleDataAvailable', event);
      if (event.data && event.data.size > 0) {
        recordedBlobs.push(event.data);
      }
    }

    function Play() {
        if (!isRecorderReady) {
          console.log("录音器还未初始化完成,请稍后再试");
          return;
        }
        console.log("play");
        recordedBlobs = [];
        mediaRecorder.start();
        console.log(mediaRecorder.state);
    }    

    function Stop() {
        if (!isRecorderReady || mediaRecorder.state !== "recording") {
          return;
        }
        console.log("stop");
        mediaRecorder.stop();
    }    

    function downloadVideo() {
        console.log('recorded blob length:', recordedBlobs.length);
        var blob = new Blob(recordedBlobs, {type: "audio/webm; codecs=opus"}); 
        console.log('new blob size:', blob.size);
        const url = window.URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.style.display = 'none';
        a.href = url;
        a.download = 'test.webm';
        document.body.appendChild(a);
        a.click();
        setTimeout(() => {
           document.body.removeChild(a);
           window.URL.revokeObjectURL(url);
        }, 100);
    }
</script>
补充说明

如果需要录制时按时间片生成切片,可以在调用mediaRecorder.start()时传入时间片参数,比如mediaRecorder.start(1000)代表每1秒触发一次dataavailable事件推送1秒长度的音频分片,不影响最终下载逻辑。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 23:40:37