在网页中直接录制麦克风声音早已不是难事,核心依靠浏览器提供的媒体采集与音频处理能力。通过getUserMedia获取音视频流,再结合MediaRecorder与Web Audio API,就能在不需要任何插件的情况下完成录音。下面先看基础结构示意图。

一、录音涉及的核心接口
实现HTML录音主要用到三个部分。第一是navigator.mediaDevices.getUserMedia,它负责向用户申请麦克风权限并返回媒体流(MediaStream)。第二是MediaRecorder API,用于将媒体流录制为 Blob 数据。第三是Web Audio API中的AudioContext与MediaStreamAudioSourceNode,用来对音频做进一步处理,比如实时监控音量或添加滤波。
很多人把Web Audio API和MediaRecorder混淆。简单来说,getUserMedia拿到原始流;Web Audio API是一套音频处理图,可以分析或加工声音;MediaRecorder则是编码器,把流写成文件。如果只是单纯录音保存,MediaRecorder已经足够;若要边录边做降噪或波形显示,就要把流接入AudioContext。
1.1 权限与兼容性
getUserMedia必须在安全上下文运行,也就是HTTPS或localhost。在普通HTTP页面里,浏览器会直接拒绝。另外,移动端iOS的Safari对录音支持较晚,需要用户手势触发,不能页面加载就自动调起。
建议在使用前先检测接口是否存在,避免老浏览器直接报错。可以用typeof navigator.mediaDevices.getUserMedia判断,不存在时给出友好提示。
二、基础录音实现步骤
下面给出一个最小可用的录音示例。点击开始按钮申请权限并录制,点击停止后生成可播放的音频地址。
// 录音基础示例
let mediaRecorder;
let recordedChunks = [];
async function startRecord() {
try {
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
mediaRecorder = new MediaRecorder(stream);
recordedChunks = [];
mediaRecorder.ondataavailable = function(e) {
if (e.data && e.data.size > 0) {
recordedChunks.push(e.data);
}
};
mediaRecorder.onstop = function() {
const blob = new Blob(recordedChunks, { type: 'audio/webm' });
const url = URL.createObjectURL(blob);
const audio = document.createElement('audio');
audio.controls = true;
audio.src = url;
document.body.appendChild(audio);
// 停止所有轨道,释放麦克风
stream.getTracks().forEach(track => track.stop());
};
mediaRecorder.start();
console.log('录音开始');
} catch (err) {
console.error('获取麦克风失败:', err);
}
}
function stopRecord() {
if (mediaRecorder && mediaRecorder.state !== 'inactive') {
mediaRecorder.stop();
console.log('录音结束');
}
}
上面代码里,MediaRecorder的ondataavailable事件会在有数据产生时触发,我们把分片存进数组。停止时合并成Blob并生成Object URL,插入页面即可播放。
要注意type: 'audio/webm'并非所有浏览器都支持,Chrome多输出webm,Firefox也支持ogg。可以通过MediaRecorder.isTypeSupported('audio/webm')做特性判断,再决定 mimeType。
2.1 用Web Audio API监控音量
如果希望在录音时显示音量条,可把流交给AudioContext分析。
async function startWithVolume() {
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
const source = audioCtx.createMediaStreamSource(stream);
const analyser = audioCtx.createAnalyser();
analyser.fftSize = 256;
source.connect(analyser);
const dataArray = new Uint8Array(analyser.frequencyBinCount);
function draw() {
analyser.getByteFrequencyData(dataArray);
let sum = 0;
for (let i = 0; i < dataArray.length; i++) {
sum += dataArray[i];
}
const avg = sum / dataArray.length;
console.log('当前平均音量:', avg);
requestAnimationFrame(draw);
}
draw();
}
这段代码创建MediaStreamAudioSourceNode并连到AnalyserNode,用getByteFrequencyData拿到频域数据算出平均能量。它不影响MediaRecorder的录制,只是并行监听。
在移动端,AudioContext经常处于 suspended 状态,需要在用户点击后调用audioCtx.resume()才能正常工作,这一点容易被忽略。
三、停止、导出与上传
录音完成后,除了本地播放,通常还要上传到服务器。下面示例把Blob用FormData发送。
function uploadRecorded(blob) {
const formData = new FormData();
formData.append('file', blob, 'record.webm');
fetch('https://ipipp.com/upload', {
method: 'POST',
body: formData
})
.then(res => res.json())
.then(data => console.log('上传成功', data))
.catch(err => console.error('上传失败', err));
}
上传时文件名后缀最好与Blob的type对应。若服务端只收wav,可借助Web Audio API的decodeAudioData解码后再用ScriptProcessor或AudioWorklet重采样导出PCM,不过那属于进阶处理。
另外,用户拒绝授权时,err.name通常是NotAllowedError,应当提示手动开启权限;设备被占用则是NotFoundError或DevicesNotFoundError,需要引导检查其他软件是否占麦。
3.1 常见误区
一个典型错误是忘记停止track,导致麦克风一直亮灯。调用mediaRecorder.stop()并不会自动关闭硬件采集,必须遍历stream.getTracks()并调stop()。
还有人把<input>标签的file类型误认为能录声音,实际上它只能选文件。真正的录音入口就是getUserMedia加Recorder,页面上只需普通button即可。
四、小结与建议
HTML录音功能整体不复杂,但细节很多。优先用MediaRecorder完成采集,有处理需求再引入Web Audio API。上线前务必在HTTPS下测试,并覆盖Chrome、Firefox与iOS Safari。把错误名分类提示,能大幅降低用户投诉。
如果项目对格式要求严格,建议录制完在后端统一转码,前端只负责拿到流并稳定上传,这样兼容性与维护成本都更低。
Web_Audio_APIMediaRecordergetUserMedia修改时间:2026-08-10 02:00:32