PHP浏览器在线预览文件(含MP4)速度过慢问题求助
解决PHP在线预览长视频加载缓慢的问题
哥们,你的问题根源太明确了——没处理HTTP Range请求!浏览器播放长视频时,会自动发分段请求(带Range请求头),只拿当前需要的视频片段,实现“边看边加载”。但你的代码不管这套,每次都把50分钟的视频全量返回,浏览器只能硬等整个文件下载完才能播放,自然慢得离谱。而直接访问文件时,Apache自动处理了Range请求,所以速度快得多。
接下来我给你改代码,加上Range请求支持,同时区分在线预览和下载两种场景:
修改后的完整代码
<?php // 自动引入部分(保留原有逻辑) //Part of auto_include.php Start $root = $_SERVER['DOCUMENT_ROOT']; include($root.'/engine/conn_db.php'); //Part of auto_include.php End $file_key = $_GET['file_key']; $file_exist = false; // 查询文件信息并验证实际存在性 if($result = $db->query("SELECT * FROM `files` WHERE `id_key`='{$file_key}'")) { if($row = $result->fetch_object()) { $file_key = $row->id_key; $file_name = $row->name; $file_size = $row->size; $file_mime = $row->mime; $file_path_rel = '/get/'.$file_key.'/'.$file_name; $file_path = $root.$file_path_rel; $file_exist = file_exists($file_path); // 实际校验文件是否存在 } $result->free(); } if(!$file_exist) { http_response_code(404); die("文件不存在"); } // 通过URL参数区分在线预览/下载模式 $is_download = isset($_GET['download']) && $_GET['download'] == 1; // 处理HTTP Range请求,实现分段加载 $handle = fopen($file_path, 'rb'); if(!$handle) { http_response_code(500); die("无法打开文件"); } $actual_file_size = filesize($file_path); // 确保文件大小准确 $start = 0; $end = $actual_file_size - 1; // 解析Range请求头 if(isset($_SERVER['HTTP_RANGE'])) { if(preg_match('/bytes=(\d+)-(\d*)/', $_SERVER['HTTP_RANGE'], $matches)) { $start = intval($matches[1]); $end = !empty($matches[2]) ? intval($matches[2]) : $actual_file_size - 1; $end = min($end, $actual_file_size - 1); // 防止超出文件范围 http_response_code(206); // 返回部分内容状态码 } else { // 无效Range请求,返回416状态码 http_response_code(416); header("Content-Range: bytes */$actual_file_size"); fclose($handle); die(); } } // 设置通用响应头 header('Accept-Ranges: bytes'); header('Cache-Control: max-age=0, no-store'); header('Connection: Keep-Alive'); header('Keep-Alive: timeout=10, max=100'); header('Server: Apache'); // 根据模式设置内容类型 if($is_download) { // 下载模式:触发浏览器保存文件 header("Content-Type: application/octet-stream"); header("Content-Disposition: attachment; filename=\"$file_name\""); } else { // 预览模式:返回文件原生MIME类型 header("Content-Type: $file_mime"); } // 设置分段响应的长度和范围头 $output_length = $end - $start + 1; header("Content-Length: $output_length"); if(isset($_SERVER['HTTP_RANGE'])) { header("Content-Range: bytes $start-$end/$actual_file_size"); } // 分块输出文件,避免内存溢出 $chunk_size = 1024 * 1024; // 1MB分块 $remaining = $output_length; fseek($handle, $start); while($remaining > 0 && !feof($handle)) { $read_size = min($chunk_size, $remaining); echo fread($handle, $read_size); $remaining -= $read_size; // 强制刷新输出缓冲,避免卡顿 ob_flush(); flush(); } fclose($handle); ?>
关键改进点说明
- 支持Range请求:解析浏览器的分段请求,只返回当前需要的视频片段,实现边看边缓冲,和直接访问文件速度一致
- 分块输出:用
fread分块读取文件,避免一次性把大文件塞进内存,防止内存溢出 - 区分预览/下载:通过
?download=1参数切换模式,下载时不再需要等全量加载,而是直接触发浏览器的分段下载流程 - 完善错误处理:增加文件存在性校验、文件打开失败、无效Range请求的处理,返回标准HTTP状态码
- 准确文件大小:用
filesize()获取实际文件大小,避免数据库存储值和实际文件不一致的问题
使用方式
- 在线预览:直接访问URL,比如
yourdomain.com/yourscript.php?file_key=xxx,浏览器会自动分段加载视频 - 下载:在URL后加参数
?download=1,比如yourdomain.com/yourscript.php?file_key=xxx&download=1,触发浏览器保存文件
这样修改后,长视频在线预览的速度就会和直接访问文件一样快了,浏览器能按需加载片段,不用等整个文件下载完。
内容的提问来源于stack exchange,提问作者DIES
相关产品推荐
相关产品推荐

