9、录制实战:录制并下载视频文件、录制并下载音频文件、录制时长控制
好,咱们进入实战环节。前面几章我们把摄像头、麦克风、屏幕共享都搞定了,数据也通过 MediaRecorder 接住了。但光接住没用,你得能把它存下来,变成实实在在的文件。
这一章,我们就来解决三个核心问题:怎么录视频并下载、怎么录纯音频并下载、以及怎么控制录制时长。说白了,就是让用户点一下“开始录”,再点一下“停止录”,然后浏览器自动弹出一个下载框。
嗯,这里要注意,不同浏览器对 MediaRecorder 的支持细节不太一样。我去年做一个在线面试平台时就踩过坑——Chrome 上跑得好好的,到了 Firefox 上录出来的文件就是打不开。所以咱们今天写的代码,我会尽量兼容主流浏览器。
9.1 录制视频并下载
先来个最直接的:从摄像头获取视频流,录制 5 秒,然后下载成 WebM 文件。
核心逻辑其实就三步:
- 创建
MediaRecorder实例,绑定视频流 - 监听
ondataavailable事件,把数据块存起来 - 停止录制后,把数据块拼成 Blob,生成下载链接
来看代码:
// 1. 获取视频流
const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
// 2. 创建 MediaRecorder
const recorder = new MediaRecorder(stream, {
mimeType: 'video/webm;codecs=vp9' // 我习惯用 vp9,压缩率更高
});
let chunks = [];
// 3. 收集数据块
recorder.ondataavailable = (e) => {
if (e.data.size > 0) {
chunks.push(e.data);
}
};
// 4. 录制结束后生成文件
recorder.onstop = () => {
const blob = new Blob(chunks, { type: 'video/webm' });
const url = URL.createObjectURL(blob);
// 创建下载链接
const a = document.createElement('a');
a.href = url;
a.download = `recording_${Date.now()}.webm`;
a.click();
// 释放内存
URL.revokeObjectURL(url);
chunks = [];
};
// 5. 开始录制
recorder.start();
// 6. 5秒后停止
setTimeout(() => {
recorder.stop();
stream.getTracks().forEach(track => track.stop());
}, 5000);
关键点:
ondataavailable默认每 1 秒触发一次(取决于timeslice参数)。如果不传 timeslice,则只在stop()时触发一次。- 我建议始终传一个 timeslice,比如 1000ms。这样即使录制中途崩溃,你至少能拿到部分数据。
mimeType要检查浏览器是否支持。可以用MediaRecorder.isTypeSupported()做降级。
小技巧: 下载文件名里带上时间戳,避免重名覆盖。我一般用 Date.now() 或者 new Date().toISOString()。
9.2 录制纯音频并下载
有时候你只需要声音,比如录一段语音笔记、或者会议录音。这时候就别开摄像头了,省资源。
做法几乎一样,只是 getUserMedia 只请求音频,并且 mimeType 要换成音频格式。
// 只获取音频流
const audioStream = await navigator.mediaDevices.getUserMedia({ audio: true });
// 检查支持的音频格式
let audioMimeType = 'audio/webm;codecs=opus';
if (!MediaRecorder.isTypeSupported(audioMimeType)) {
audioMimeType = 'audio/webm'; // 降级方案
}
const audioRecorder = new MediaRecorder(audioStream, {
mimeType: audioMimeType
});
let audioChunks = [];
audioRecorder.ondataavailable = (e) => {
if (e.data.size > 0) {
audioChunks.push(e.data);
}
};
audioRecorder.onstop = () => {
const blob = new Blob(audioChunks, { type: audioMimeType });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `audio_${Date.now()}.webm`;
a.click();
URL.revokeObjectURL(url);
audioChunks = [];
};
// 开始录制
audioRecorder.start();
// 录制 10 秒
setTimeout(() => {
audioRecorder.stop();
audioStream.getTracks().forEach(track => track.stop());
}, 10000);
注意: 纯音频录制时,mimeType 不要写成 video/webm。虽然有些浏览器能兼容,但录出来的文件可能包含空的视频轨道,导致播放器识别异常。我曾经因为这个被测试妹子追着骂了一下午……
9.3 录制时长控制
实际项目中,你不可能让用户一直录下去。要么限制最大时长,要么提供暂停/恢复功能。这里我讲三种常用方案。
方案一:定时器强制停止
最简单粗暴。适合固定时长录制,比如录 30 秒短视频。
const MAX_DURATION = 30000; // 30秒
recorder.start();
const timer = setTimeout(() => {
recorder.stop();
}, MAX_DURATION);
// 如果用户提前停止,记得清除定时器
recorder.onstop = () => {
clearTimeout(timer);
// ... 后续处理
};
方案二:实时计时 + 进度显示
用户能看到录了多久,快到限制时给个提示。这个体验更好。
let elapsed = 0;
const MAX_DURATION = 60; // 秒
const interval = setInterval(() => {
elapsed++;
console.log(`已录制 ${elapsed} 秒`);
if (elapsed >= MAX_DURATION) {
recorder.stop();
clearInterval(interval);
}
}, 1000);
recorder.onstop = () => {
clearInterval(interval);
};
方案三:基于数据量的控制
有时候你更关心文件大小,而不是时长。比如限制录出来的文件不超过 10MB。
let totalSize = 0;
const MAX_SIZE = 10 * 1024 * 1024; // 10MB
recorder.ondataavailable = (e) => {
if (e.data.size > 0) {
totalSize += e.data.size;
chunks.push(e.data);
if (totalSize >= MAX_SIZE) {
recorder.stop();
}
}
};
我的建议: 实际项目中最好把方案二和方案三结合。既限制时长,又限制大小。比如“最长录制 5 分钟,文件不超过 50MB”。哪个先达到就停哪个。
9.4 本章知识体系
下面这张图帮你理清整个录制流程的核心脉络:
9.5 避坑指南
最后分享几个我实际踩过的坑,希望能帮你省点时间。
- Blob 类型不匹配: 下载时
a.download的后缀名要和 Blob 的type一致。比如video/webm对应.webm,audio/mp4对应.mp4。否则用户下载后可能打不开。 - 内存泄漏: 每次生成
URL.createObjectURL后,记得在下载完成后调用URL.revokeObjectURL。否则标签页开久了,内存会越占越多。 - 停止流: 录制结束后,一定要调用
stream.getTracks().forEach(track => track.stop())释放摄像头和麦克风。不然摄像头灯一直亮着,用户会慌。 - 移动端兼容: 在 iOS Safari 上,
MediaRecorder支持有限。我建议在移动端用audio/mp4或video/mp4格式,兼容性更好。
总结一下: 录制并下载的核心就三件事——拿到流、用 MediaRecorder 录、拼成 Blob 下载。时长控制可以按需选择定时器、计时器或数据量限制。代码量不大,但细节不少。多测几个浏览器,准没错。