1. 程式人生 > >PHP + NGINX 控制視頻文件播放,並防止文件下載

PHP + NGINX 控制視頻文件播放,並防止文件下載

header tar 使用 val run rewrite token 解密 回文

最簡單的方法是使用NGINX的 internal 功能

server {
listen 80;
server_name www.xxx.com;
 
location / {
index index.php index.html index.htm;
root /xxx;

if (!-e $request_filename) {
rewrite ^/index.php(.*)$ /index.php?s=$1 last;
rewrite ^(.*)$ /index.php?s=$1 last;
break;
}
}

 # 這裏使用internal做下載防護,只允許內部程序(PHP等)訪問,這樣外部直接訪問這個地址就會提示404錯誤
location ~ \.mp4$ {
internal;
  # 這裏的路徑配置是可選的,可以配置到網站外部,和其他location裏的配置路徑是一個意思,可以更好的防止文件被通過網址下載
root /bbb;
}

location ~ \.php$ {
root /xxx;
try_files $uri =404;
fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}

然後在PHP中通過header(‘Location: /xxx/aa.mp4‘)訪問,但這個只適合於小文件,因為這種方式不支持Content-Range和HTTP 206,導致不支持斷點續傳和視頻文件的邊下邊播

最好的方式是通過header(‘X-Accel-Redirect: /xxx/aa.mp4‘)訪問,X-Accel-Redirect支持Content-Range和HTTP 206,Apache裏面是X-Sendfile

如果不能配置nginx的internal選項,或者nginx不支持X-Accel-Redirect,卻要使用斷點續傳、邊下邊播和下載防護,那麽就要通過PHP代碼來控制

思路上是使用token鑒權,首先下載文件前由前端請求一個token,這個token由用戶ID+時間戳+視頻ID組成,然後存儲到session並加密後發送給前端

$token = $_SESSION[‘uid‘] . ‘|‘ . time() . ‘|‘ . $id;
// 保存未加密token到seesion
$_SESSION[‘video_token‘] = $token;
// 加密token以便發送到客戶端
$token = Mcrypt::encode($token, ‘lbnnbs‘);

前端收到token,向下載控制器網址發送token請求文件下載,控制器對token解碼,判斷Session是否一致、用戶ID是否正確、是否超時、視頻ID是否正確

然後將視頻ID轉換為文件地址,通過PHP讀取文件發送給前端下載。並通過發送Content-Range和HTTP 206來支持斷點續傳、邊下邊播等操作

$id = decodeIdFromToken($token);

$file = getFilePath($id);

SendVideo($file);

    private function SendVideo($file) {
        header("Content-type: video/mp4");
        header("Accept-Ranges: bytes");

        $size = filesize($file);
        if (isset($_SERVER[‘HTTP_RANGE‘])) {
            header("HTTP/1.1 206 Partial Content");
            list($name, $range) = explode("=", $_SERVER[‘HTTP_RANGE‘]);
            list($begin, $end) = explode("-", $range);
            if ($end == 0)
                $end = $size - 1;
        }else {
            $begin = 0;
            $end = $size - 1;
        }
        header("Content-Length: " . ($end - $begin + 1));
        header("Content-Disposition: filename=" . basename($file));
        header("Content-Range: bytes " . $begin . "-" . $end . "/" . $size);

        $fp = fopen($file, ‘rb‘);
        fseek($fp, $begin);
        while (!feof($fp)) {
            $p = min(1024, $end - $begin + 1);
            $begin += $p;
            echo fread($fp, $p);
        }
        fclose($fp);
    }

    private function decodeIdFromToken($token) {
        if (empty($_SESSION[‘uid‘])) {
            return false;
        }

        $token = Mcrypt::decode($token, ‘lbnnbs‘);

        if ($token != $_SESSION[‘video_token‘]) {
            //token解密失敗,判定失效
            return false;
        }


        $token_arr = explode(‘|‘, $token);

        if (intval($token_arr[0]) != $_SESSION[‘uid‘]) {
            // token不是當前用戶,判定失效
            return false;
        }

        if (intval($token_arr[1]) < time() - 10) {
            // token生成於10秒前,判定失效
            return false;
        }

        unset($_SESSION[‘video_token‘]);

        return $token_arr[2]; // 返回id
    }

另外需要註意的是,很多時候mp4文件會無法支持邊下邊播,這是因為在轉碼或者壓縮視頻文件到mp4的時候把文件的元數據移除了或者放到了文件的末尾,導致前端播放器不能第一時間獲取到視頻文件的播放時長等信息,只能全部下載完畢後或者直到讀取到了元數據後才能播放。這個時候可以使用qt-faststart.exe工具進行處理,把元數據放回文件頭部即可。

PHP + NGINX 控制視頻文件播放,並防止文件下載