如何获取PHP中readfile传输远程视频文件时的已传输内容大小
实现方案
你当前使用readfile()直接输出远程文件的方式没有中间统计的切入点,同时也没有处理视频播放常见的范围请求(用户拖动进度条时浏览器发起的Range请求),不仅无法统计传输量,还可能出现播放拖动异常的问题。
核心实现思路
- 不直接调用
readfile(),改用逐块读取远程文件流、逐块输出到客户端的方式,每输出一块就累加已传输字节数 - 增加Range请求的支持,兼容视频拖动播放的场景,保证统计数据和实际传输内容匹配
- 检测客户端连接状态,即使客户端中途中断播放,也能正常记录最终已传输的字节数
- 统计结果可以根据业务需要存入日志、数据库或Session中
可直接运行的实现代码
<?php // 远程视频地址 $file = 'https://example.com/?rud27pxppu45zk.mp4'; // 获取远程文件头信息 $head = array_change_key_case(get_headers($file, TRUE)); $totalSize = $head['content-length']; $mimeType = $head['content-type'] ?? 'video/mp4'; // 初始化传输计数 $transferred = 0; // 可在此处自定义统计前置逻辑,比如初始化数据库记录、绑定用户标识等 // 处理视频拖动的Range请求 $start = 0; $end = $totalSize - 1; if (isset($_SERVER['HTTP_RANGE'])) { list(, $range) = explode('=', $_SERVER['HTTP_RANGE'], 2); if (str_contains($range, ',')) { header('HTTP/1.1 416 Requested Range Not Satisfiable'); header("Content-Range: bytes */$totalSize"); exit; } if (str_starts_with($range, '-')) { $start = $totalSize - intval(substr($range, 1)); } else { $range = explode('-', $range); $start = intval($range[0]); $end = (isset($range[1]) && is_numeric($range[1])) ? intval($range[1]) : $end; } $end = ($end > $totalSize - 1) ? $totalSize - 1 : $end; if ($start > $end || $start >= $totalSize || $end < 0) { header('HTTP/1.1 416 Requested Range Not Satisfiable'); header("Content-Range: bytes */$totalSize"); exit; } header('HTTP/1.1 206 Partial Content'); header("Content-Range: bytes $start-$end/$totalSize"); $length = $end - $start + 1; } else { $length = $totalSize; } // 输出基础响应头 header('Content-Type: ' . $mimeType); header('Accept-Ranges: bytes'); header('Content-Disposition: inline'); header('Content-Length: ' . $length); // 打开远程文件流 $remoteStream = fopen($file, 'rb'); fseek($remoteStream, $start); // 逐块读取输出并计数 $blockSize = 1024 * 8; // 单次读取8KB,可根据服务器性能调整,块越小统计精度越高 while (!feof($remoteStream) && $transferred < $length && !connection_aborted()) { $buffer = fread($remoteStream, $blockSize); echo $buffer; flush(); ob_flush(); $transferred += strlen($buffer); } // 关闭远程流 fclose($remoteStream); // 统计结果处理逻辑,可自定义存入数据库、日志等 // 示例:写入本地日志 file_put_contents('./video_transfer_log.txt', date('Y-m-d H:i:s') . " 已传输字节数:{$transferred}/{$totalSize}\n", FILE_APPEND); exit;
注意事项
- 服务器需要开启
allow_url_fopen配置才能正常读取远程HTTP文件流,如果该配置被关闭,可以改用curl的CURLOPT_WRITEFUNCTION回调实现逐块读取,在回调中完成输出和计数即可 - 如果需要区分不同用户的传输数据,统计时可以绑定用户ID、Session ID等唯一标识
- 远程视频地址如果存放在第三方存储服务,要保证服务器请求该资源时不会触发防盗链拦截
内容的提问来源于stack exchange,提问作者Alexandru Popa
相关产品推荐
相关产品推荐

