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

使用MediaSource与FileReader遇重载解析失败,FileReader.onload未执行求助

问题原因分析与修复方案

编写代码尝试将输入音频文件生成圆形波形图,但上传音频时出现两个问题:

  1. 报错:Failed to execute 'appendBuffer' on 'SourceBuffer': Overload resolution failed,触发位置为第85行
  2. FileReader.onload函数从未执行

原代码:

<!DOCTYPE html>
    <html>
      <head>
        <meta charset="utf-8" />
        <title>Circular Waveform Example</title>
        <script src="https://d3js.org/d3.v5.min.js"></script>
      </head>
      <body>
        <input type="file" id="audio-file" accept="audio/*" />
        <br /><br />
        <canvas id="waveform"></canvas>
        <script>
          // function to generate circular waveform image
          function generateCircularWaveform(audioFile, canvasId) {
            const canvas = document.getElementById(canvasId);
            const canvasContext = canvas.getContext("2d");
            const audioContext = new AudioContext();
            const mediaSource = new MediaSource();
            const audioElement = document.createElement("audio");
            audioElement.controls = false;
            audioElement.style.display = "none";
            document.body.appendChild(audioElement);

            let audioData = [];
            let chunkSize = 1024 * 1024; // 1MB

            mediaSource.addEventListener("sourceopen", function(e) {
              console.log("Media sourceopen starting");

              let sourceBuffer = mediaSource.addSourceBuffer("audio/mpeg");

              let fileReader = new FileReader();
              let offset = 0;

              fileReader.onload = function() {
                audioContext.decodeAudioData(this.result, function(buffer) {
                  let channelData = buffer.getChannelData(0);

                  for (let i = 0; i < channelData.length; i += 100) {
                    let magnitude = Math.abs(channelData[i]) * canvas.height;
                    let angle = (i / channelData.length) * 2 * Math.PI;
                    let x = magnitude * Math.cos(angle);
                    let y = magnitude * Math.sin(angle);

                    audioData.push({ x: x, y: y });
                  }

                  if (offset < audioFile.size) {
                    offset += chunkSize;
                    let chunk = audioFile.slice(offset, offset + chunkSize);
                    fileReader.readAsArrayBuffer(chunk);
                  } else {
                    let waveformPath = new Path2D();
                    waveformPath.moveTo(audioData[0].x, audioData[0].y);

                    for (let i = 1; i < audioData.length; i++) {
                      waveformPath.lineTo(audioData[i].x, audioData[i].y);
                    }

                    canvasContext.lineWidth = 1;
                    canvasContext.strokeStyle = "black";
                    canvasContext.stroke(waveformPath);

                    document.body.removeChild(audioElement);
                  }
                });
              };

              mediaSource.addEventListener("sourceended", function() {
                console.log("Media source ended");
              });

              mediaSource.addEventListener("sourceclose", function() {
                console.log("Media source closed");
              });

              mediaSource.addEventListener("error", function(e) {
                console.error("Media source error:", e);
              });

              sourceBuffer.mode = "sequence";
              sourceBuffer.timestampOffset = 0;
              sourceBuffer.appendBuffer(audioFile.slice(0, chunkSize));
              audioElement.play();
            });

            canvasContext.fillStyle = "yellow";
            canvasContext.fillRect(0, 0, canvas.width, canvas.height);
          }

          // handle file
          // input change event listener
          const inputElement = document.getElementById("audio-file");
          inputElement.addEventListener("change", function(event) {
            const file = event.target.files[0];
            generateCircularWaveform(file, "waveform");
          });
        </script>
      </body>
    </html>

问题原因

1. appendBuffer报错的核心原因

  • SourceBuffer.appendBuffer()要求传入ArrayBuffer类型的数据,但你直接传入了File.slice()返回的Blob对象,类型不匹配导致重载解析失败。
  • 硬编码了audio/mpeg作为SourceBuffer的MIME类型,若用户上传的是WAV、OGG等其他格式音频,会因格式不兼容触发错误。

2. FileReader.onload未执行的原因

  • 仅定义了fileReader.onload回调函数,但从未调用fileReader.readAsArrayBuffer()触发文件读取流程,回调自然不会执行。
  • 完全没必要引入MediaSource和audioElement来处理音频解码:生成波形只需要解析音频的PCM数据,直接用FileReader读取文件后通过AudioContext.decodeAudioData解码即可,多余的媒体播放逻辑反而干扰了正常流程。

修复后的代码

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>Circular Waveform Example</title>
    <script src="https://d3js.org/d3.v5.min.js"></script>
</head>
<body>
    <input type="file" id="audio-file" accept="audio/*" />
    <br /><br />
    <canvas id="waveform" width="400" height="400"></canvas>
    <script>
        // 生成圆形波形图
        function generateCircularWaveform(audioFile, canvasId) {
            const canvas = document.getElementById(canvasId);
            const canvasContext = canvas.getContext("2d");
            const audioContext = new AudioContext();

            // 初始化画布背景
            canvasContext.fillStyle = "yellow";
            canvasContext.fillRect(0, 0, canvas.width, canvas.height);
            // 将画布原点移到中心,方便绘制圆形波形
            canvasContext.translate(canvas.width / 2, canvas.height / 2);

            const fileReader = new FileReader();
            
            fileReader.onload = function() {
                audioContext.decodeAudioData(this.result, function(buffer) {
                    const channelData = buffer.getChannelData(0);
                    const audioData = [];
                    // 采样间隔,控制波形点数量
                    const sampleStep = 100;

                    for (let i = 0; i < channelData.length; i += sampleStep) {
                        // 归一化音量并映射到画布半径范围
                        const magnitude = Math.abs(channelData[i]) * (canvas.height / 2);
                        const angle = (i / channelData.length) * 2 * Math.PI;
                        const x = magnitude * Math.cos(angle);
                        const y = magnitude * Math.sin(angle);

                        audioData.push({ x, y });
                    }

                    // 绘制波形路径
                    const waveformPath = new Path2D();
                    waveformPath.moveTo(audioData[0].x, audioData[0].y);

                    for (let i = 1; i < audioData.length; i++) {
                        waveformPath.lineTo(audioData[i].x, audioData[i].y);
                    }

                    canvasContext.lineWidth = 1;
                    canvasContext.strokeStyle = "black";
                    canvasContext.stroke(waveformPath);
                }, function(error) {
                    console.error("音频解码失败:", error);
                });
            };

            // 触发文件读取
            fileReader.readAsArrayBuffer(audioFile);
        }

        // 监听文件选择事件
        const inputElement = document.getElementById("audio-file");
        inputElement.addEventListener("change", function(event) {
            const file = event.target.files[0];
            if (file) {
                generateCircularWaveform(file, "waveform");
            }
        });
    </script>
</body>
</html>

修复说明

  1. 移除了多余的MediaSource和audioElement,简化流程
  2. 调用fileReader.readAsArrayBuffer(audioFile)触发文件读取,确保onload回调执行
  3. 将画布原点移到中心,更符合圆形波形的绘制逻辑
  4. 添加了解码失败的错误回调,方便排查问题
  5. 动态适配画布尺寸,避免波形显示异常

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 11:12:04