JavaScript: разрабатываем приложение для записи звука

Привет, друзья!
В этом небольшом «туториале» я хочу показать вам, как разработать приложение для записи и воспроизведения аудио-файлов.
Функционал нашего приложения будет следующим:
- запись аудио
- отображение записи с возможностью ее предварительного прослушивания и последующего сохранения или удаления
- хранение аудио-файлов на сервере
- извлечение аудио-файлов, хранящихся на сервере, и их отображение в браузере
Основная технология, которую мы будем использовать, это MediaDevices. Данная технология входит в состав глобального объекта Navigator. Основным методом, предоставляемым указанным интерфейсом является getUserMedia(). Запись данных (в простых случаях вроде нашего) выполняется с помощью интерфейса MediaRecorder.
Интерфейс MediaDevices на сегодняшний день поддерживается всеми современными браузерами.
Для небольшой стилизации нашего приложения мы будем использовать Sass.
Выглядеть приложение примерно так:
Исходный код приложения находится здесь.
Основным источником вдохновения для меня послужила эта замечательная статья.
Обратите внимание: данная статья рассчитана, преимущественно, на начинающих разработчиков, хотя, смею надеяться, что и опытные найдут в ней что-нибудь интересное.
Вы готовы? Тогда вперед!
Начнем с разработки сервера.
Сервер
Создаем директорию для проекта, переходим в нее, инициализируем проект и устанавливаем необходимые зависимости:
# создаем директорию
mkdir record-app
# переходим в нее
cd !$
# инициализируем проект
# `-y` означает выбор значений по умолчанию
yarn init -y
# или
npm init -y
# устанавливаем основные зависимости
yarn add express multer
# или
npm i ...
# устанавливаем зависимости для разработки
yarn add concurrently nodemon open-cli sass
Зависимости:
express— фреймворк дляNode.js, облегчающий разработкуREST APImulter— обертка надbusboyдля разбора данных в форматеmultipart/form-data, часто используемая для сохранения файловconcurrently— утилита для одновременного выполнения команд, определенных вpackage.jsonnodemon— утилита для запуска сервера для разработки (сервера, который автоматически перезапускается при изменении наблюдаемых файлов)open-cli— утилита для автоматического открытия вкладки браузера по указанному адресуsass— утилита для преобразованияSASSвCSS
Определяем команды для запуска сервера для разработки в package.json:
"scripts": {
"sass": "sass --no-source-map --watch public/style.scss:public/style.css",
"server": "open-cli http://localhost:3000 && nodemon index.js",
"start": "concurrently \"yarn sass\" \"yarn server\""
}
Команды:
- команда
sassзапускает утилиту для преобразования файлаpublic/style.scssв файлpublic/style.cssв режиме реального времени (--watch) и без создания карты источников (--no-source-map) - команда
serverзапускает сервер для разработки и открывает вкладку браузера по адресуhttp://localhost:3000 - команда
startвыполняет командыsassиserver
В коде сервера мы будем использовать ES6-модули (import/export), поэтому определим в package.json тип основного файла сервера как модуль:
"type": "module"
В качестве альтернативы для кода сервера можно использовать файл с расширением .mjs.
Давайте определимся со структурой проекта:
- record-app
- node_modules
- public - директория со статическими файлами (клиент)
- img
- microphone.png
- pause.png
- play.png
- stop.png
- index.html
- script.js
- style.scss
- uploads - директория для аудио-файлов
- index.js - сервер
- package.json
Изображения, которые мы будем использовать при разработке приложения, можно скачать здесь.
Приступаем к реализации.
Что должен уметь наш сервер? Многого от него не требуется: все, что он должен уметь — это обслуживать статические файлы из директорий public и uploads, а также сохранять и извлекать аудио-файлы.
Импортируем необходимые модули:
import express from 'express'
import { dirname } from 'path'
import { fileURLToPath } from 'url'
import { promises as fs } from 'fs'
import multer from 'multer'
Определяем абсолютный путь к текущей (рабочей) директории:
const __dirname = dirname(fileURLToPath(import.meta.url))
Настраиваем multer:
const upload = multer({
storage: multer.diskStorage({
// директория для файлов
destination(req, file, cb) {
cb(null, 'uploads/')
},
// названия файлов
filename(req, file, cb) {
cb(null, `${file.originalname}.mp3`)
}
})
})
Создаем приложение express и определяем директории со статическими файлами:
const app = express()
app.use(express.static('public'))
app.use(express.static('uploads'))
Определяем маршрут для сохранения аудио с помощью multer, используемого в качестве посредника (middleware). Аудио будет отправляться методом POST в формате multipart/form-data по адресу /save:
// поле, содержащее файл, должно называться `audio`
app.post('/save', upload.single('audio'), (req, res) => {
// в ответ мы возвращаем статус `201`,
// свидетельствующий об успешном сохранении файла на сервере
res.sendStatus(201)
})
Определяем «роут» для извлечения сохраненных аудио и их отправки клиенту (метод — GET, адрес — /records):
app.get('/records', async (req, res) => {
try {
// читаем содержимое директории `uploads`
let files = await fs.readdir(`${__dirname}/uploads`)
// нас интересуют только файлы с расширением `.mp3`
files = files.filter((fileName) => fileName.split('.')[1] === 'mp3')
// отправляем файлы клиенту
res.status(200).json(files)
} catch (e) {
console.log(e)
}
})
Наконец, определяем порт (по умолчанию 3000) и запускаем сервер:
const port = process.env.PORT || 3000
app.listen(port, () => {
console.log('
