媒体捕获和流API(又称MediaStream API)允许你从用户的麦克风中录制音频,然后将录制的音频或媒体元素作为轨道获得。然后你可以在录制后直接播放这些音轨,或者将媒体上传到你的服务器上。
在本教程中,我们将创建一个网站,使用媒体流API让用户录制一些东西,然后将录制的音频上传到服务器保存。用户也将能够看到并播放所有上传的录音。
你可以在这个GitHub存储库中找到本教程的完整代码。
设置服务器
我们首先要创建一个Node.js和Express服务器。所以首先要确保下载并安装Node.js,如果你的机器上没有的话。
创建一个目录
创建一个新的目录,用来存放项目,并切换到该目录。
mkdir recording-tutorial
cd recording-tutorial
初始化项目
然后,用npm初始化该项目。
npm init -y
选项-y ,用默认值创建package.json 。
安装依赖项
接下来,我们将为我们创建的服务器安装Express和nodemon,以便在有任何变化时重启服务器。
npm i express nodemon
创建Express服务器
现在我们可以开始创建一个简单的服务器。在项目的根目录下创建index.js ,内容如下。
const path = require('path');
const express = require('express');
const app = express();
const port = process.env.PORT || 3000;
app.use(express.static('public/assets'));
app.listen(port, () => {
console.log(`App listening at http://localhost:${port}`);
});
这就创建了一个服务器,除非在环境中设置了端口,否则它将在端口3000 上运行,并且它暴露了一个目录public/assets --我们将很快创建这个目录--它将存放JavaScript和CSS文件以及图片。
添加一个脚本
最后,在package.json ,在scripts 下添加一个start 脚本。
"scripts": {
"start": "nodemon index.js"
},
启动网络服务器
让我们测试一下我们的服务器。运行下面的程序来启动服务器。
npm start
而服务器应该在3000端口启动。你可以尝试在localhost:3000 ,但你会看到一个消息说 "无法GET /",因为我们还没有定义任何路由。
创建录音页面
接下来,我们将创建一个页面,作为网站的主页面。用户将使用这个页面来录制、查看和播放录音。
创建public 目录,并在里面创建一个index.html 文件,内容如下。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Record</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-KyZXEAg3QhqLMpG8r+8fhAXLRk2vvoC2f3B09zVXn8CA5QIVfZOJ3BCsw2P0p/We" crossorigin="anonymous">
<link href="/css/index.css" rel="stylesheet" />
</head>
<body class="pt-5">
<div class="container">
<h1 class="text-center">Record Your Voice</h1>
<div class="record-button-container text-center mt-5">
<button class="bg-transparent border btn record-button rounded-circle shadow-sm text-center" id="recordButton">
<img src="/images/microphone.png" alt="Record" class="img-fluid" />
</button>
</div>
</div>
</body>
</html>
这个页面使用Bootstrap 5进行风格设计。现在,该页面只是显示一个按钮,用户可以用它来录音。
请注意,我们正在使用一个图片作为麦克风。你可以在Iconscout上下载该图标,也可以使用GitHub仓库中的修改版本。
下载该图标并将其放置在public/assets/images 内,名称为microphone.png 。
添加样式
我们也要链接样式表index.css ,所以创建一个public/assets/css/index.css ,内容如下。
.record-button {
height: 8em;
width: 8em;
border-color: #f3f3f3 !important;
}
.record-button:hover {
box-shadow: 0 .5rem 1rem rgba(0,0,0,.15)!important;
}
创建路由
最后,我们只需要在index.js 中添加新的路由。在app.listen 前添加以下内容。
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, 'public/index.html'));
});
如果服务器还没有运行,用npm start ,启动服务器。然后在浏览器中转到localhost:3000 。你会看到一个记录按钮。

现在,这个按钮什么也不做。我们将需要绑定一个点击事件,以触发录音。
创建一个public/assets/js/record.js 文件,内容如下。
//initialize elements we'll use
const recordButton = document.getElementById('recordButton');
const recordButtonImage = recordButton.firstElementChild;
let chunks = []; //will be used later to record audio
let mediaRecorder = null; //will be used later to record audio
let audioBlob = null; //the blob that will hold the recorded audio
我们正在初始化我们以后要使用的变量。然后创建一个record 函数,它将是recordButton 的点击事件的事件监听器。
function record() {
//TODO start recording
}
recordButton.addEventListener('click', record);
我们也要把这个函数作为事件监听器附加到记录按钮上。
媒体录制
为了开始录制,我们需要使用mediaDevices.getUserMedia()方法。
这个方法允许我们获得一个流,只有在用户为网站提供许可的情况下,才能录制用户的音频和/或视频。getUserMedia 方法允许我们访问本地输入设备。
getUserMedia 该方法接受一个MediaStreamConstraints对象作为参数,该对象包括一组约束条件,指定我们将从 。这些约束条件可以是具有布尔值的音频和视频。getUserMedia
如果值为false,意味着我们对访问这个设备或录制这个媒体不感兴趣。
getUserMedia 返回一个承诺。如果用户允许网站录音,承诺的履行程序会收到一个MediaStream对象,我们可以用它来媒体捕获用户的视频或音频流。
媒体捕获和流
为了使用MediaStream API对象来捕捉媒体轨迹,我们需要使用MediaRecorder接口。我们需要创建一个该接口的新对象,该对象在构造函数中接受MediaStream对象,并允许我们通过其方法轻松控制录制。
在record 函数中,添加以下内容。
//check if browser supports getUserMedia
if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
alert('Your browser does not support recording!');
return;
}
// browser supports getUserMedia
// change image in button
recordButtonImage.src = `/images/${mediaRecorder && mediaRecorder.state === 'recording' ? 'microphone' : 'stop'}.png`;
if (!mediaRecorder) {
// start recording
navigator.mediaDevices.getUserMedia({
audio: true,
})
.then((stream) => {
mediaRecorder = new MediaRecorder(stream);
mediaRecorder.start();
mediaRecorder.ondataavailable = mediaRecorderDataAvailable;
mediaRecorder.onstop = mediaRecorderStop;
})
.catch((err) => {
alert(`The following error occurred: ${err}`);
// change image in button
recordButtonImage.src = '/images/microphone.png';
});
} else {
// stop recording
mediaRecorder.stop();
}
浏览器支持
我们首先要检查navigator.mediaDevices 和navigator.mediaDevices.getUserMedia 是否被定义,因为有些浏览器如Internet Explorer、Android上的Chrome或其他浏览器并不支持它。
此外,使用getUserMedia 需要安全的网站,这意味着要么是使用HTTPS加载的页面,file:// ,要么是从localhost 。因此,如果页面没有安全加载,mediaDevices 和getUserMedia 将被未定义。
开始记录
如果条件是假的(也就是说,mediaDevices 和getUserMedia 都支持),我们首先要把录音按钮的图像改为stop.png ,你可以从Iconscout或GitHub仓库下载,并把它放在public/assets/images 。
然后,我们要检查mediaRecorder (我们在文件的开头定义的)是否为空。
如果它是空的,就意味着没有正在进行的录制。所以,我们使用getUserMedia 得到一个MediaStream实例来开始录制。
我们传递给它一个只有键audio 和值true 的对象,因为我们只是在录制音频。
这就是浏览器提示用户允许网站访问麦克风的地方。如果用户允许,履行处理程序中的代码将被执行。
mediaRecorder = new MediaRecorder(stream);
mediaRecorder.start();
mediaRecorder.ondataavailable = mediaRecorderDataAvailable;
mediaRecorder.onstop = mediaRecorderStop;
这里我们要创建一个新的MediaRecorder ,把它分配给我们在文件开头定义的mediaRecorder 。
我们把从getUserMedia 收到的数据流传给构造器。然后,我们使用mediaRecorder.start()开始录制。
最后,我们将事件处理程序(我们将很快创建)与两个事件绑定,dataavailable 和stop 。
我们还添加了一个catch 处理程序,以防用户不允许网站访问麦克风或可能抛出的任何其他异常。
停止录音
如果mediaRecorder 不为空,这一切都会发生。如果它是空的,这意味着有一个正在进行的录音,而用户正在结束它。所以,我们使用mediaRecorder.stop()方法来停止录音。
} else {
//stop recording
mediaRecorder.stop();
}
