You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 03:36:20