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.jsfs.createReadStream的参数要求。 - 兼容无Range头请求:当浏览器不带Range头时,返回完整视频(200 OK),避免直接返回错误中断加载。
- 异步文件检查:用
promisify包装fs.stat,避免同步操作阻塞事件循环,同时增加视频存在性校验。 - 完善错误捕获:在
getVideoStream内部添加异常捕获,处理文件不存在、数据库查询失败等场景。
内容的提问来源于stack exchange,提问作者Sayeed Mahdi Mousavi
相关产品推荐
相关产品推荐

