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

Next.js 13.4视频流无法播放问题求助

Next.js视频流无法播放仅显示大小的问题排查与修复

问题根源分析

你的视频能显示大小但无法播放,核心是后端API存在几个关键错误:

  • Range头解析逻辑错误:用正则去掉所有非数字字符的方式,会破坏Range头的格式结构(比如bytes=0-1000会被转成01000),导致起始字节计算完全错误。
  • 流读取参数名错误:fs.createReadStream的范围参数是start和end,而非你写的chunkStart和chunkEnd,导致流没有正确读取指定片段的内容。
  • 未处理无Range头的请求:部分浏览器初始请求可能不带Range头,直接返回400错误会导致视频加载流程中断。

修复方案

1. 前端代码调整(解决自动播放限制)

浏览器对自动播放有严格规则,添加muted属性可以提升自动播放成功率:

<video className="w-full rounded-lg" controls autoPlay muted>
  <source src={`/api/video/${film._id}`} type="video/mp4" />
</video>

2. 后端API完整修复

修改getVideoStream函数中的关键错误点:

import connectDB from "@/config/connectDB"
import { Film } from "@/models"
import { NextResponse } from "next/server"
import fs from "fs"
import { promisify } from "util"

const CHUNK_SIZE = 1000000 // 1MB
const stat = promisify(fs.stat)
connectDB()

async function getVideoStream(req, id) {
  try {
    const film = await Film.findById(id)
    if (!film) return new Response("视频不存在", { status: 404 })
    
    const filePath = film.storage
    const fileStat = await stat(filePath)
    const fileSize = fileStat.size

    const range = req.headers.get("range")
    if (!range) {
      // 无Range头时返回完整视频
      const headers = {
        "Content-Length": fileSize.toString(),
        "Content-Type": "video/mp4",
      }
      return new Response(fs.createReadStream(filePath), { status: 200, headers })
    }

    // 正确解析Range头:拆分bytes=后的起始/结束字节
    const [startStr, endStr] = range.replace(/bytes=/, "").split("-")
    const chunkStart = parseInt(startStr, 10) || 0
    const chunkEnd = endStr 
      ? parseInt(endStr, 10)
      : Math.min(chunkStart + CHUNK_SIZE, fileSize - 1)

    const contentLength = chunkEnd - chunkStart + 1

    const headers = {
      "Content-Range": `bytes ${chunkStart}-${chunkEnd}/${fileSize}`,
      "Accept-Ranges": "bytes",
      "Content-Length": contentLength.toString(),
      "Content-Type": "video/mp4",
    }

    // 使用正确的start/end参数创建读取流
    const videoStream = fs.createReadStream(filePath, { start: chunkStart, end: chunkEnd })
    return new Response(videoStream, { status: 206, headers })
  } catch (error) {
    console.error("视频流错误:", error)
    return new Response("服务器内部错误", { status: 500 })
  }
}

export async function GET(request, { params }) {
  try {
    return getVideoStream(request, params?.id)
  } catch (error) {
    return NextResponse.json({ error: error.message }, { status: 400 })
  }
}

关键修复点说明

  • Range头正确解析:拆分bytes=后的内容,分别提取起始和结束字节,兼容bytes=0-这种只指定起始位置的格式。
  • 修正流参数名:将chunkStart/chunkEnd改为start/end,匹配Node.js fs.createReadStream的参数要求。
  • 兼容无Range头请求:当浏览器不带Range头时,返回完整视频(200 OK),避免直接返回错误中断加载。
  • 异步文件检查:用promisify包装fs.stat,避免同步操作阻塞事件循环,同时增加视频存在性校验。
  • 完善错误捕获:在getVideoStream内部添加异常捕获,处理文件不存在、数据库查询失败等场景。

内容的提问来源于stack exchange,提问作者Sayeed Mahdi Mousavi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 11:38:14