
本文介绍如何使用 Node.js 和 Express 构建一个音频流式服务,自动遍历指定目录中的 MP3 文件并顺序拼接为连续可播放的 HTTP 流,替代单文件 res.sendFile() 的静态响应方式。
本文介绍如何使用 node.js 和 express 构建一个音频流式服务,自动遍历指定目录中的 mp3 文件并顺序拼接为连续可播放的 http 流,替代单文件 `res.sendfile()` 的静态响应方式。
要实现类似在线电台的“自动轮播”功能,核心在于将多个音频文件无缝串联为一个持续输出的 Readable Stream,并通过 res.write() 逐块传输给客户端(如 HTML5 <audio></audio> 标签),而非一次性发送单个文件。res.sendFile() 仅适用于静态资源分发,无法满足动态拼接与流控需求。
以下是完整、可运行的实现方案:
✅ 步骤一:引入必要模块并配置基础服务
const express = require('express');
const path = require('path');
const fs = require('fs').promises;
const { Readable, PassThrough } = require('stream'); // 注意:需显式引入 Readable 和 PassThrough
const app = express();
const port = 8001;
const playlistDir = path.join(__dirname, 'public/playlists/gral/');✅ 步骤二:构建连续音频流生成器
我们封装一个 createContinuousAudioStream(dirPath) 函数,它会:
根据 package.json 中的 engines 字段自动检测 Node.js 版本需求,未安装 nvm 时自动安装并切换至对应版本。支持 Windows、macOS、Linux。适用于 Node.js 项目开发、版本切换和环境配置。
- 自动读取目录下所有
.mp3文件(按字母序,可扩展为自定义排序); - 使用
fs.createReadStream逐个打开文件; - 利用
Readable自定义流逻辑,在前一个文件结束时自动切换至下一个; - 错误发生时跳过当前文件,继续播放后续内容,保障服务健壮性。
async function createContinuousAudioStream(dirPath) {
const files = (await fs.readdir(dirPath))
.filter(f => f.toLowerCase().endsWith('.mp3'))
.sort(); // 按文件名排序(如需按修改时间等,可改用 fs.stat)
if (files.length === 0) {
throw new Error(`No MP3 files found in ${dirPath}`);
}
let currentIndex = 0;
return new Readable({
read() {
if (currentIndex >= files.length) {
this.push(null); // 流结束
return;
}
const filePath = path.join(dirPath, files[currentIndex]);
const fileStream = fs.createReadStream(filePath);
fileStream.on('data', (chunk) => this.push(chunk));
fileStream.on('end', () => {
currentIndex++;
// 自动触发下一轮 read(),无需手动 resume()
});
fileStream.on('error', (err) => {
console.warn(`⚠️ Skip broken file: ${files[currentIndex]}, error:`, err.message);
currentIndex++;
// 继续下一轮
});
}
});
}? 关键说明:
Readable的read()方法是“拉模式”入口,每次被调用时负责推送一个 chunk 或触发新文件读取。我们不调用fileStream.resume()(已废弃),而是依赖fileStream自身事件驱动完成切换。
✅ 步骤三:在路由中返回流式响应
确保设置正确的 Content-Type 和 Content-Disposition,并禁用缓存以支持流式播放:
app.get('/stream', async (req, res) => {
try {
const audioStream = await createContinuousAudioStream(playlistDir);
// 关键响应头:告知浏览器这是音频流(MP3),支持边下边播
res.setHeader('Content-Type', 'audio/mpeg');
res.setHeader('Transfer-Encoding', 'chunked');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
// 管道传输:流 → 响应
audioStream.pipe(res);
} catch (err) {
console.error('❌ Stream initialization failed:', err);
res.status(500).send('Failed to start audio stream');
}
});✅ 步骤四:前端播放示例(public/index.html)
<!DOCTYPE html>
<html>
<head><title>MP3 Radio Stream</title></head>
<body>
<h2>Live Audio Stream</h2>
<audio controls autoplay>
<source src="/stream" type="audio/mpeg">
Your browser does not support the audio element.
</audio>
</body>
</html>⚠️ 注意事项与优化建议
- 内存安全:本方案采用流式管道,不会将整个音频文件加载到内存,适合大文件和长时间运行。
-
文件排序:当前按字典序排序(如
01.mp3,02.mp3)。如需按创建时间/元数据排序,请在fs.readdir后结合fs.stat过滤。 -
格式兼容性:
Content-Type: audio/mpeg适配 MP3;若含其他格式(如.ogg,.wav),需统一转换或动态检测 MIME 类型(推荐使用file-type库)。 -
并发控制:生产环境建议添加限流(如
express-rate-limit)和连接数监控,防止资源耗尽。 -
错误恢复:当前实现跳过损坏文件。如需重试机制,可在
fileStream.on('error')中加入指数退避重试逻辑。
通过以上改造,你的 Node.js 服务即可真正实现“目录即电台”的自动化音频流广播——无需重启、无需手动干预,文件增删即刻生效。

















