在Express服务器获取音频时长并传递至客户端的实现方案咨询
在Express服务器获取音频时长并传递至客户端的实现方案咨询
当然可以实现啦!我给你两种实用的方案,你可以根据自己的业务场景来选择:
方案一:实时解析音频流获取时长(适合低频请求场景)
这种方案不需要提前存储额外数据,每次请求时直接解析音频文件的元数据拿到时长,推荐用纯JS库music-metadata来做,不用依赖外部工具,非常方便。
步骤1:安装依赖
在项目根目录执行命令:
npm install music-metadata
步骤2:修改你的Express路由代码
我们需要先解析音频流拿到时长,再把时长放到响应头里,最后重新打开一个流把音频内容返回给客户端(因为解析元数据会消费掉原流):
const mm = require('music-metadata'); const { ObjectId } = require('mongoose').Types; server.get('/play', async (req, res) => { const gridFSBucket = new mongoose.mongo.GridFSBucket(conn.db); const dataType = "audio/mp3"; res.set('Content-Type', dataType); if (req.query.afn) { const filename = req.query.afn; try { // 先打开流解析元数据 const tempStream = gridFSBucket.openDownloadStreamByName(filename); const metadata = await mm.parseStream(tempStream, { mimeType: dataType, size: Infinity }); const duration = metadata.format.duration; // 时长单位:秒 // 把时长放入自定义响应头 res.set('X-Audio-Duration', duration.toString()); // 重新打开流返回音频内容 const downloadStream = gridFSBucket.openDownloadStreamByName(filename); res.set('Content-Disposition', `inline; filename="${filename}"`); downloadStream.pipe(res); } catch (err) { console.error('解析音频元数据出错:', err); res.status(500).send('获取音频信息失败'); } } if (req.query.id) { const recordID = new ObjectId(req.query.id); try { const tempStream = gridFSBucket.openDownloadStream(recordID); const metadata = await mm.parseStream(tempStream, { mimeType: dataType, size: Infinity }); const duration = metadata.format.duration; res.set('X-Audio-Duration', duration.toString()); const downloadStream = gridFSBucket.openDownloadStream(recordID); res.set('Content-Disposition', `inline; filename=APP-Audio`); downloadStream.pipe(res); } catch (err) { console.error('解析音频元数据出错:', err); res.status(500).send('获取音频信息失败'); } } });
方案二:存储音频时提前保存时长(适合高频请求场景)
如果你的音频会被频繁请求,每次都解析流会浪费资源,最好在上传音频到GridFS的时候就把时长存到对应的文件元数据里,后续请求直接读取即可。
步骤1:上传时保存时长
假设你有上传音频的路由,修改它来解析时长并存储:
const mm = require('music-metadata'); server.post('/upload', async (req, res) => { const gridFSBucket = new mongoose.mongo.GridFSBucket(conn.db); const uploadStream = gridFSBucket.openUploadStream(req.file.originalname); try { // 解析上传的音频流获取时长 const metadata = await mm.parseStream(req.file.stream, { mimeType: req.file.mimetype }); const duration = metadata.format.duration; // 上传完成后,把时长更新到GridFS的文件文档中 uploadStream.on('finish', async () => { await conn.db.collection('fs.files').updateOne( { _id: uploadStream.id }, { $set: { duration: duration } } ); }); req.file.stream.pipe(uploadStream); uploadStream.on('finish', () => { res.send('音频上传成功'); }); } catch (err) { console.error('解析音频时长出错:', err); res.status(500).send('上传失败'); } });
步骤2:播放路由直接读取时长
现在播放时不需要再解析音频,直接从GridFS的文件集合里拿时长:
const { ObjectId } = require('mongoose').Types; server.get('/play', async (req, res) => { const gridFSBucket = new mongoose.mongo.GridFSBucket(conn.db); const dataType = "audio/mp3"; res.set('Content-Type', dataType); if (req.query.afn) { const filename = req.query.afn; // 查询GridFS文件文档获取时长 const fileDoc = await conn.db.collection('fs.files').findOne({ filename }); if (!fileDoc) { return res.status(404).send('音频不存在'); } res.set('X-Audio-Duration', fileDoc.duration.toString()); // 返回音频流 const downloadStream = gridFSBucket.openDownloadStreamByName(filename); res.set('Content-Disposition', `inline; filename="${filename}"`); downloadStream.pipe(res); } if (req.query.id) { const recordID = new ObjectId(req.query.id); const fileDoc = await conn.db.collection('fs.files').findOne({ _id: recordID }); if (!fileDoc) { return res.status(404).send('音频不存在'); } res.set('X-Audio-Duration', fileDoc.duration.toString()); const downloadStream = gridFSBucket.openDownloadStream(recordID); res.set('Content-Disposition', `inline; filename=APP-Audio`); downloadStream.pipe(res); } });
客户端如何获取时长?
不管用哪种方案,客户端都可以通过读取响应头来拿到时长,比如用Fetch API的示例:
fetch('/play?afn=your-audio-file.mp3') .then(response => { // 从响应头获取时长并转成数字 const duration = parseFloat(response.headers.get('X-Audio-Duration')); console.log('音频时长(秒):', duration); // 处理音频播放 return response.blob(); }) .then(blob => { const audio = new Audio(URL.createObjectURL(blob)); audio.play(); }) .catch(err => console.error('请求出错:', err));
备注:内容来源于stack exchange,提问作者Michel
相关产品推荐
相关产品推荐

