如何在Node.js服务器中实时保存摄像头流及Blob块为视频文件
如何在Node.js服务器中实时保存摄像头流及Blob块为单个视频文件
首先得指出你当前代码里的核心问题:axios默认会把请求体序列化成JSON,所以你传的Uint8Array会被转成普通的数字数组,服务端拿到的根本不是二进制数据,自然没法直接拼成视频文件。接下来我给你一步步调整代码,实现完整的录制保存流程。
第一步:修正客户端的Blob发送方式
咱们要直接发送原始的Blob二进制数据,而不是转成Uint8Array再用JSON传输。修改你的postBlob和新增录制结束的函数:
// 发送单个Blob数据块 postBlob = async (blob) => { try { await axios.post('/api', blob, { headers: { 'Content-Type': blob.type, // 用Blob自带的类型,比如video/webm 'X-Chunk-Type': 'video-data' // 自定义头标记这是视频数据块 } }); } catch (err) { console.error('数据块上传失败:', err); } }; // 录制结束时给服务端发结束信号 finishRecording = async () => { try { const res = await axios.post('/api/end-recording', {}, { headers: { 'X-Chunk-Type': 'end-signal' } }); console.log('录制完成,文件已保存:', res.data.filePath); } catch (err) { console.error('结束录制请求失败:', err); } };
这样客户端就会直接把Blob的二进制内容发出去,服务端能拿到原始的视频数据。
第二步:服务端实现分块写入与结束处理
服务端需要用可写流来持续接收并写入数据块,直到收到结束信号再关闭流,生成完整的视频文件。这里用express.raw()来处理二进制数据,而不是默认的JSON解析:
const express = require('express'); const fs = require('fs'); const path = require('path'); const app = express(); // 配置express处理视频类型的二进制数据 app.use(express.raw({ type: 'video/*' })); // 全局维护一个可写流(单用户场景,多用户需用会话/ID区分文件) let videoWriteStream = null; const outputDir = path.join(__dirname, 'recorded-videos'); // 确保输出目录存在 if (!fs.existsSync(outputDir)) fs.mkdirSync(outputDir); // 处理视频数据块 app.post('/api', (req, res) => { const chunkType = req.headers['x-chunk-type']; if (chunkType === 'video-data') { // 如果还没创建写入流,初始化一个新文件 if (!videoWriteStream) { const fileName = `video-${Date.now()}.webm`; const filePath = path.join(outputDir, fileName); videoWriteStream = fs.createWriteStream(filePath, { flags: 'w' }); // 监听写入错误,避免程序崩溃 videoWriteStream.on('error', (err) => { console.error('文件写入错误:', err); videoWriteStream = null; }); } // 将当前数据块写入文件 videoWriteStream.write(req.body); res.status(200).json({ status: 'chunk-received' }); } else { res.status(400).json({ error: '无效的数据块类型' }); } }); // 处理录制结束信号 app.post('/api/end-recording', (req, res) => { if (videoWriteStream) { videoWriteStream.end(() => { const filePath = videoWriteStream.path; console.log('视频录制完成,文件路径:', filePath); videoWriteStream = null; // 重置流,准备下一次录制 res.status(200).json({ status: 'success', filePath }); }); } else { res.status(400).json({ error: '没有正在进行的录制' }); } }); app.listen(3000, () => { console.log('服务器运行在 http://localhost:3000'); });
关键注意点
- 多用户场景适配:上面的代码是单用户版本,如果要支持多个用户同时录制,需要给每个用户分配唯一的文件(比如用用户ID、会话ID作为文件名的一部分),避免文件被覆盖。
- 视频格式兼容性:确保客户端录制的视频格式是WebM或者MP4这类流式拼接友好的格式,不然拼接后的文件可能无法播放。
- 错误处理:一定要给可写流加上
error监听,防止磁盘满、权限不足等问题导致程序崩溃。
内容的提问来源于stack exchange,提问作者Nane
相关产品推荐
相关产品推荐

