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

Next.js应用MongoDB GridFSBucket MP4视频流无法播放问题排查

Next.js中GridFS视频流无法播放的问题排查与修复

问题描述

在Next.js应用中尝试通过getServerSideProps()实现MongoDB GridFSBucket中MP4视频的流播放,Chrome浏览器中视频播放器正常显示,但视频无法播放(时长显示为0:00)。日志出现警告warn - You should not access 'res' after getServerSideProps resolves.,无其他错误信息。

原代码

export const getServerSideProps: GetServerSideProps = async ({
    res,
    query: { hash }
}) => {        
    const database = await mongodb()
    const Videos = database.collection('videos')

    const { fileId } = await Videos.findOne({ uid: hash })

    const Files = new GridFSBucket(database)
    const id = new ObjectId(fileId)

    const file: GridFSFile = await new Promise((resolve, reject) => {
        Files.find({
            _id: id
        }).toArray((err, files) => {
            if (err) reject(err)
            resolve(files[0])
        })
    })
    const { contentType } = file || {}

    res.writeHead('Content-Type', contentType) // contentType is "video/mp4"
    Files.openDownloadStream(id)
    .on('data', (chunk) => {
        res.write(chunk)
    })
    .on('end', () => {
        res.end()
    })
    .on('error', (err) => {
        throw err
    })

    return {
        props: {}
    }
}

根本性错误分析

  1. getServerSideProps使用场景错误:该函数设计目的是为页面获取渲染所需Props,而非直接处理响应流。你在函数内直接操作res写入视频数据,同时返回props,会导致Next.js继续执行页面渲染流程,与流操作冲突,既触发警告,又导致响应内容混乱,播放器无法解析。

  2. res.writeHead参数错误:res.writeHead第一个参数必须是HTTP状态码(如200),第二个参数才是响应头对象。原代码将'Content-Type'作为第一个参数,导致响应头设置完全错误,浏览器无法识别视频类型。

  3. 未支持范围请求(Range):视频播放器需要通过Range请求实现分段加载、解析时长,原代码未处理该请求,也未设置Accept-Ranges、Content-Length等必需响应头,这是时长显示0:00且无法播放的核心原因。

  4. 错误处理不规范:流的error事件中直接抛出异常,会导致未捕获错误,无法正确返回错误响应。

修复方案:改用API路由处理视频流

Next.js推荐使用API路由处理这类响应流场景,以下是修复后的代码示例:

import type { NextApiRequest, NextApiResponse } from 'next'
import { mongodb } from '../../lib/mongodb' // 替换为你的MongoDB连接路径
import { GridFSBucket, GridFSFile } from 'mongodb'
import { ObjectId } from 'mongodb'

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  const { hash } = req.query
  if (!hash || typeof hash !== 'string') {
    res.status(400).json({ error: '无效的哈希值' })
    return
  }

  try {
    const database = await mongodb()
    const Videos = database.collection('videos')
    const videoDoc = await Videos.findOne({ uid: hash })
    
    if (!videoDoc?.fileId) {
      res.status(404).json({ error: '视频不存在' })
      return
    }

    const Files = new GridFSBucket(database)
    const id = new ObjectId(videoDoc.fileId)
    const file: GridFSFile = await new Promise((resolve, reject) => {
      Files.find({ _id: id }).toArray((err, files) => {
        if (err) reject(err)
        resolve(files[0])
      })
    })

    if (!file) {
      res.status(404).json({ error: '文件不存在' })
      return
    }

    const { contentType, length } = file
    const range = req.headers.range

    if (range) {
      // 处理范围请求,支持视频分段加载
      const parts = range.replace(/bytes=/, '').split('-')
      const start = parseInt(parts[0], 10)
      const end = parts[1] ? parseInt(parts[1], 10) : length - 1
      const chunksize = end - start + 1

      res.writeHead(206, {
        'Content-Range': `bytes ${start}-${end}/${length}`,
        'Accept-Ranges': 'bytes',
        'Content-Length': chunksize,
        'Content-Type': contentType || 'video/mp4',
      })

      const stream = Files.openDownloadStream(id, { start, end })
      stream.pipe(res)
    } else {
      // 处理完整视频请求
      res.writeHead(200, {
        'Content-Length': length,
        'Content-Type': contentType || 'video/mp4',
        'Accept-Ranges': 'bytes',
      })
      const stream = Files.openDownloadStream(id)
      stream.pipe(res)
    }
  } catch (err) {
    console.error('视频流错误:', err)
    res.status(500).json({ error: '服务器内部错误' })
  }
}

// 禁用Next.js默认的响应大小限制,支持大视频流
export const config = {
  api: {
    responseLimit: false,
  },
}

修复说明

  • 改用API路由专门处理视频流,符合Next.js设计规范,避免与页面Props获取逻辑冲突。
  • 正确处理Range请求,实现视频分段加载,解决时长显示异常问题。
  • 修复res.writeHead参数错误,正确设置响应头。
  • 增加完整的错误处理,返回对应HTTP状态码。
  • 设置responseLimit: false,避免Next.js限制大视频的响应大小。

内容的提问来源于stack exchange,提问作者user3142695

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 14:36:13