Node.js中转MJPEG流至前端:如何在Next.js展示Axis摄像头直播?
解决方案:通过服务器代理转发MJPEG流并在前端展示
1. 在Next.js中创建API代理路由
通过自建API路由转发Axis摄像头的MJPEG流,解决生产环境混合内容(HTTPS页面加载HTTP资源)的限制。
Pages Router 版本(适用于Next.js 12及更早)
在pages/api/camera-stream.js中编写代理逻辑:
import { createProxyMiddleware } from 'http-proxy-middleware'; export const config = { api: { bodyParser: false, // 禁用body解析,保留二进制流格式 externalResolver: true, }, }; const proxy = createProxyMiddleware({ target: process.env.AXIS_CAMERA_IP, // 从环境变量读取摄像头IP changeOrigin: true, pathRewrite: { '^/api/camera-stream': '/axis-cgi/mjpg/video.cgi', // 重写路径到Axis的MJPEG接口 }, onProxyReq: (proxyReq, req) => { // 传递前端传入的参数(分辨率、帧率、压缩比) const query = new URLSearchParams(req.url.split('?')[1]); proxyReq.path += `?${query.toString()}`; // 若摄像头需要认证,添加Authorization头 // proxyReq.setHeader('Authorization', 'Basic ' + Buffer.from(`${process.env.AXIS_USER}:${process.env.AXIS_PWD}`).toString('base64')); }, onProxyRes: (proxyRes, req, res) => { // 转发MJPEG所需的响应头,确保前端能识别流格式 res.setHeader('Content-Type', proxyRes.headers['content-type']); res.setHeader('Cache-Control', 'no-cache'); res.setHeader('Connection', 'keep-alive'); // 移除可能导致缓存的冗余头 delete proxyRes.headers['cache-control']; delete proxyRes.headers['connection']; }, }); export default function handler(req, res) { proxy(req, res); }
App Router 版本(适用于Next.js 13+)
在app/api/camera-stream/route.js中实现流转发:
import { NextResponse } from 'next/server'; import http from 'http'; export async function GET(request) { const { searchParams } = new URL(request.url); // 读取前端参数,设置默认值 const resolution = searchParams.get('resolution') || '1280x720'; const fps = searchParams.get('fps') || 15; const compression = searchParams.get('compression') || 50; const cameraIp = process.env.AXIS_CAMERA_IP; return new Promise((resolve, reject) => { const cameraUrl = `http://${cameraIp}/axis-cgi/mjpg/video.cgi?resolution=${resolution}&fps=${fps}&compression=${compression}`; const req = http.get(cameraUrl, (cameraRes) => { const headers = new Headers(); headers.set('Content-Type', cameraRes.headers['content-type']); headers.set('Cache-Control', 'no-cache'); headers.set('Connection', 'keep-alive'); // 将摄像头的流直接转发给前端 resolve(new NextResponse(cameraRes, { headers })); }); req.on('error', () => { reject(new NextResponse('获取摄像头流失败', { status: 500 })); }); }); }
2. 前端页面展示MJPEG流
在Next.js页面组件中,通过安全的API路由地址加载流:
import { useState } from 'react'; export default function CameraStreamPage() { const [streamParams, setStreamParams] = useState({ resolution: '1280x720', fps: 15, compression: 50, }); // 拼接API路由地址,携带参数 const streamUrl = `/api/camera-stream?resolution=${streamParams.resolution}&fps=${streamParams.fps}&compression=${streamParams.compression}`; return ( <div className="stream-container"> <h2>Axis摄像头直播流</h2> <img src={streamUrl} alt="Live Camera Feed" style={{ maxWidth: '100%', height: 'auto', border: '1px solid #eee' }} /> {/* 可选:添加参数调整控件 */} <div style={{ marginTop: '20px', gap: '10px', display: 'flex', flexWrap: 'wrap' }}> <div> <label>分辨率:</label> <select value={streamParams.resolution} onChange={(e) => setStreamParams({...streamParams, resolution: e.target.value})} > <option value="640x480">640x480</option> <option value="1280x720">1280x720</option> <option value="1920x1080">1920x1080</option> </select> </div> <div> <label>帧率:</label> <input type="number" min="1" max="30" value={streamParams.fps} onChange={(e) => setStreamParams({...streamParams, fps: e.target.value})} /> </div> </div> </div> ); }
关键注意事项
- 敏感信息管理:摄像头IP、账号密码等敏感数据必须放在
.env.local环境变量中,禁止硬编码。 - 认证处理:若Axis摄像头开启了Basic认证,需在代理路由中添加
Authorization头,示例代码已标注位置。 - 流格式兼容:确保服务器端禁用body解析,避免二进制流被破坏;转发响应头时保留
multipart/x-mixed-replace类型。
内容的提问来源于stack exchange,提问作者Myles Jefferson
相关产品推荐
相关产品推荐

