codeigniter需手动实现视频流式响应,因readfile()和框架输出机制不支持range请求、断点续播及大文件分块传输;须绕过缓冲、原生写入php://output,并严格设置206状态与content-range等头部。

CodeIgniter 本身不内置视频流式响应能力,response->setContentType() 和 readfile() 只能做简单文件输出,遇到大视频、断点续播、自适应码率等需求时会卡死或 500。真要支持视频流,得绕过框架默认输出机制,手动控制 HTTP 分块和头部。
为什么不能直接用 readfile() 输出视频文件
因为 readfile() 是全量读取再吐出,内存爆满、超时、无法支持 Range 请求(拖拽/续播就失效)。浏览器发来 Range: bytes=1024-2047,你却返回整个文件 + 200 状态,前端就会卡住或报错 ERR_CONNECTION_RESET。
- CI 的
output->set_output()会缓存全部内容,根本没法分块传 -
Content-Length必须精确,但流式场景下你不知道总长(比如合并多个远程片段) - 必须手动设置
Accept-Ranges: bytes、Content-Range、206 Partial Content等头,CI 默认不碰这些
如何在 CodeIgniter 中实现基础视频流(MP4 / WebM)
核心是绕过 CI 的输出缓冲,用原生 PHP 流式写入 php://output,并严格按 RFC 7233 处理 Range 请求。以下逻辑应放在控制器方法中,且需关闭所有输出缓冲(ob_end_clean()):
- 先检查
$_SERVER['HTTP_RANGE']是否存在,没有则走完整响应(200 OK) - 用
fopen($video_path, 'rb')打开只读流,filesize()获取大小 - 解析
bytes=xxx-yyy,计算start、end、length - 调用
fseek()定位,循环fread()+echo,每次不超过 8192 字节 - 必须设置:
header('HTTP/1.1 206 Partial Content')、header('Accept-Ranges: bytes')、header("Content-Range: bytes $start-$end/$size")
示例关键片段:
ob_end_clean();
header('Content-Type: video/mp4');
header('Accept-Ranges: bytes');
if (isset($_SERVER['HTTP_RANGE'])) {
list(, $range) = explode('=', $_SERVER['HTTP_RANGE'], 2);
list($start, $end) = explode('-', $range . '-', 2);
$start = intval($start);
$end = $end === '' ? $size - 1 : intval($end);
$length = $end - $start + 1;
header("HTTP/1.1 206 Partial Content");
header("Content-Range: bytes $start-$end/$size");
header("Content-Length: $length");
fseek($fp, $start);
} else {
header("Content-Length: $size");
}
while (!feof($fp) && (connection_status() == CONNECTION_NORMAL)) {
echo fread($fp, 8192);
flush();
}
fclose($fp);
怎么对接远程视频源(如 S3、CDN 或分段 URL)
本地文件流只是起点。真实项目里视频常存在对象存储或边缘节点,这时不能 fopen() 本地路径。得用 cURL 或 stream_context_create() 拉取远程流,并实时转发 —— 但注意:PHP 的 fread() 不支持远程流的随机 seek,所以只能做「透传式流」,无法响应任意 Range 请求。
- 若后端是 S3,优先用预签名 URL +
302 redirect让浏览器直连,绕过 PHP 层 - 若必须经 PHP 中转(如鉴权),用
curl_setopt($ch, CURLOPT_WRITEFUNCTION, $callback)边拉边推,但无法支持拖拽(因无法反向 seek 远程流) - 合并多个远程视频片段?参考知识库里的
sequentialCombine()思路,但要在 PHP 中用php://temp或fd做内存流拼接,再分块输出
断点续播和播放记录怎么和流式响应配合
流式响应只管“怎么把字节送出去”,续播逻辑在前端。重点不是后端返回什么,而是前端如何发请求、带什么头、怎么设 video.currentTime:
- 前端必须用
fetch()+Range头请求视频,否则拖拽无效;<video src="xxx"></video>自动带 Range,但你得确保后端真处理了 - 播放记录保存接口(如
/video_progress/save)和流式接口完全解耦,别指望流响应里塞记录逻辑 - 用户跳到 120 秒,前端发
Range: bytes=1200000-,后端返回 206 + 对应字节,然后前端设video.currentTime = 120即可 - 别在流式控制器里查数据库记进度——高并发下 IO 成瓶颈,应由前端定时上报 + 后端异步落库
真正容易被忽略的是:流式响应一旦开始输出,就不能再改 header 或发 error;任何异常(如文件不存在、权限不足)必须在 fopen() 前判断清楚,否则浏览器收不到错误提示,只看到黑屏或加载中转圈。











