使用MediaSource与FileReader遇重载解析失败,FileReader.onload未执行求助
问题原因分析与修复方案
编写代码尝试将输入音频文件生成圆形波形图,但上传音频时出现两个问题:
- 报错:
Failed to execute 'appendBuffer' on 'SourceBuffer': Overload resolution failed,触发位置为第85行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>
修复说明
- 移除了多余的
MediaSource和audioElement,简化流程 - 调用
fileReader.readAsArrayBuffer(audioFile)触发文件读取,确保onload回调执行 - 将画布原点移到中心,更符合圆形波形的绘制逻辑
- 添加了解码失败的错误回调,方便排查问题
- 动态适配画布尺寸,避免波形显示异常
内容的提问来源于stack exchange,提问作者yariv bar
相关产品推荐
相关产品推荐

