PHP基于session防视频下载Safari失效及文件路径配置问题咨询
PHP视频地址隐藏脚本问题修复方案
问题根因说明
- 苹果设备/Safari无法播放:一是原代码存在
$_SSEION拼写错误,Safari对会话校验逻辑更严格,直接导致验证失败;二是视频输出逻辑未支持Range分段请求,Safari播放视频强制要求支持分段加载,原代码一次性全量输出文件不符合要求。 - 子目录路径失效:原代码文件存在检查路径和读取路径不一致的bug,校验时读取
videos/目录下的文件,实际读取的时候直接读当前目录文件,因此需要同时在两个目录存放视频才能运行。
修复后完整代码
index.php(入口文件)
<?php /** * index.php - 入口文件 **/ session_start(); session_regenerate_id(); // 修复会话变量拼写错误 $_SESSION['setToken'] = true; // 可直接指定子目录路径,例:"subdir/vid.mp4" $token = "vid.mp4"; $token_encrypted = openssl_encrypt($token, "aes128", session_id()); ?> <video width="320" height="240" controls> <!-- 补充video格式声明,兼容Safari识别 --> <source src="video.php?vid=<?php echo $token_encrypted; ?>" type="video/mp4"> </video>
video.php(跳转校验文件)
<?php /** * video.php - 跳转校验文件 **/ session_start(); $token = $_GET['vid']; $prev_sid = session_id(); if(isset($_SESSION['setToken'])) { unset($_SESSION['setToken']); $origin_token = openssl_decrypt($token, "aes128", session_id()); // 新增目录遍历防护 if(strpos($origin_token, '..') !== false) { session_regenerate_id(true); die("非法请求"); } session_regenerate_id(); $new_token = openssl_encrypt($origin_token, "aes128", $prev_sid.session_id()); // 参数做URL编码避免特殊字符异常 header("Location: access.php?id=".urlencode($prev_sid)."&vid=".urlencode($new_token)); exit; } else { session_regenerate_id(true); die("无效请求"); } ?>
access.php(视频输出文件)
<?php /** * access.php - 视频输出文件 **/ session_start(); $token = openssl_decrypt($_GET['vid'], "aes128", $_GET['id'].session_id()); session_regenerate_id(true); // 合法性校验 if(empty($token) || strpos($token, '..') !== false) { header("HTTP/1.1 403 Forbidden"); die("非法请求"); } // 统一文件路径,无需在多目录存放视频 $filePath = "videos/".$token; if(!file_exists($filePath) || !is_file($filePath)) { header("HTTP/1.1 404 Not Found"); die("Error: File Does not exist"); } $fileSize = filesize($filePath); $seekStart = 0; $seekEnd = $fileSize - 1; // 新增Range请求支持,兼容Safari分段加载要求 if(isset($_SERVER['HTTP_RANGE'])) { $range = $_SERVER['HTTP_RANGE']; list($sizeUnit, $rangeInfo) = explode('=', $range, 2); if($sizeUnit == 'bytes') { list($seekStart, $seekEnd) = explode('-', $rangeInfo); $seekStart = intval($seekStart); $seekEnd = ($seekEnd == '' ? $fileSize - 1 : intval($seekEnd)); $seekEnd = min($seekEnd, $fileSize - 1); if($seekStart > $seekEnd || $seekStart >= $fileSize) { header("HTTP/1.1 416 Requested Range Not Satisfiable"); header("Content-Range: bytes */$fileSize"); exit; } header("HTTP/1.1 206 Partial Content"); header("Content-Range: bytes $seekStart-$seekEnd/$fileSize"); } } else { header("HTTP/1.1 200 OK"); } // 输出标准响应头 header("Content-Type: video/mp4"); header("Accept-Ranges: bytes"); header("Content-Length: " . ($seekEnd - $seekStart + 1)); header("Cache-Control: no-cache, no-store, must-revalidate"); header("Pragma: no-cache"); header("Expires: 0"); // 分段输出视频内容,避免大文件内存溢出 $fp = fopen($filePath, "rb"); fseek($fp, $seekStart); $bufferSize = 1024 * 1024; while(!feof($fp) && ($pos = ftell($fp)) <= $seekEnd) { if($pos + $bufferSize > $seekEnd) { $bufferSize = $seekEnd - $pos + 1; } echo fread($fp, $bufferSize); ob_flush(); flush(); } fclose($fp); exit; ?>
内容的提问来源于stack exchange,提问作者murat tansu özyol
相关产品推荐
相关产品推荐

