2011-11-04 50 views
2

我使用php腳本檢查用戶是否在提供圖像或視頻之前登錄。實際文件存儲在無法直接訪問的文件夾中。如果認證成功,php腳本將中繼/輸出文件。我的目標是讓通過php腳本提供的文件儘可能接近實際文件的直接鏈接。如何通過php腳本提供文件並使其與文件的直接鏈接完全相同?

所以,這是交易。圖像工作正常。視頻(MP4)的工作有一些注意事項。我無法使用h264.code-shop.com流式傳輸模塊進行僞流式傳輸,並且視頻只能在iPhone上成功播放一次。一旦視頻到達結尾,我無法刷新視頻而無需刷新頁面,並收到「視頻無法加載」錯誤(JW播放器)。如果我繞過PHP腳本並直接鏈接到視頻文件,一切正常。因此,顯然我的php腳本生成的輸出與通常從直接訪問文件獲得的輸出之間有某些不同。所以,對於那裏的所有專家,我可能會錯過什麼?正確的http頭文件?我能做些什麼來使我的腳本輸出文件的方式與直接訪問文件的方式完全相同?

下面是我使用的腳本:

<?php 

if (!isset($_GET['f'])){die(header('location:../login.php'));} 
if (!isset($_GET['onlyHappensFromHTACCESS'])) { 
$_GET['f'] = "../protectedFolder/".$_GET['f']; 
$file = realpath($_GET['f']); 
$type = getFileType($file); 
if (acceptableType($type)) 
{ 
if (goodTiming()) 
    { 
    //this function used to allow navigation away from the page while video has not completely loaded 
    session_write_close(); 

    $fs = stat($file); 

    header("Content-Type: $type"); 
    header("Etag: ".sprintf('"%x-%x-%s"', $fs['ino'], $fs['size'],base_convert(str_pad($fs['mtime'],16,"0"),10,16))); 

    if (isset($_SERVER['HTTP_RANGE'])) 
    { // do it for any device that supports byte-ranges not only iPhone 
    rangeDownload($file); 
    } 
    else 
    { 
    $size = filesize($file); // File size 

    header("Content-Length: $size"); 
    header("Last-Modified: " .gmdate("D, d M Y H:i:s")." GMT"); 
    header("Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0"); 
    header("Pragma: no-cache"); 
    header("Keep-Alive: timeout=5, max=100"); 
    header("Connection: Keep-Alive"); 

    $fh = fopen($file, "rb"); 

    while (($buf=fread($fh, 1024 * 8)) != '') 
    { 
    set_time_limit(0); // Reset time limit for big files 
    echo $buf; 
    flush(); 
    } 

    fclose($fh); 
    } 
    } 
die(); 
} 
header('HTTP/1.1 403 Forbidden'); 
die(header('location:../login.php')); 
} 

function getFileType($file) { 
if (function_exists("finfo_open")) { 
$finfo = finfo_open(FILEINFO_MIME_TYPE); 
if ($file==false){$file=realpath("../authorization_failure.html");} 
$type = finfo_file($finfo, $file); 
finfo_close($finfo); 
return $type; 
} 
else { 
$types = array(
    'jpg' => 'image/jpeg', 'jpeg' => 'image/jpeg', 'pjpeg' => 'image/jpeg', 'png' => 'image/png', 
    'gif' => 'image/gif', 'bmp' => 'image/bmp', 'flv' => 'video/x-flv', 'mp4' => 'video/mp4' 
); 
$ext = substr($file, strrpos($file, '.') + 1); 
if (key_exists($ext, $types)) return $types[$ext]; 
return "unknown"; 
} 
} 


function acceptableType($type) { 
$array = array("image/jpeg", "image/jpg", "image/png", "image/png", "video/x-flv", "video/mp4"); 
if (in_array($type, $array)) 
    return true; 
return false; 
} 


function goodTiming() { 
$n = time(); 
session_start(); 
if ($n - $_SESSION['lastcheck'] > 15) 
    return false; 
return true; 
} 

function rangeDownload($file) { 

$fp = @fopen($file, 'rb'); 

$size = filesize($file); // File size 
$length = $size;   // Content length 
$start = 0;    // Start byte 
$end = $size - 1;  // End byte 
// Now that we've gotten so far without errors we send the accept range header 
/* At the moment we only support single ranges. 
* Multiple ranges requires some more work to ensure it works correctly 
* and comply with the spesifications: http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.2 
* 
* Multirange support annouces itself with: 
* header('Accept-Ranges: bytes'); 
* 
* Multirange content must be sent with multipart/byteranges mediatype, 
* (mediatype = mimetype) 
* as well as a boundry header to indicate the various chunks of data. 
*/ 
header("Accept-Ranges: 0-$length"); 
// header('Accept-Ranges: bytes'); 
// multipart/byteranges 
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.2 
if (isset($_SERVER['HTTP_RANGE'])) { 

    $c_start = $start; 
    $c_end = $end; 
    // Extract the range string 
    list(, $range) = explode('=', $_SERVER['HTTP_RANGE'], 2); 
    // Make sure the client hasn't sent us a multibyte range 
    if (strpos($range, ',') !== false) { 

     // (?) Shoud this be issued here, or should the first 
     // range be used? Or should the header be ignored and 
     // we output the whole content? 
     header('HTTP/1.1 416 Requested Range Not Satisfiable'); 
     header("Content-Range: bytes $start-$end/$size"); 
     // (?) Echo some info to the client? 
     exit; 
    } 
    // If the range starts with an '-' we start from the beginning 
    // If not, we forward the file pointer 
    // And make sure to get the end byte if spesified 
    if ($range== '-') { 

     // The n-number of the last bytes is requested 
     $c_start = $size - substr($range, 1); 
    } 
    else { 

     $range = explode('-', $range); 
     $c_start = $range[0]; 
     $c_end = (isset($range[1]) && is_numeric($range[1])) ? $range[1] : $size; 
    } 
    /* Check the range and make sure it's treated according to the specs. 
    * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html 
    */ 
    // End bytes cannot be larger than $end. 
    $c_end = ($c_end > $end) ? $end : $c_end; 
    // Validate the requested range and return an error if it's not correct. 
    if ($c_start > $c_end || $c_start > $size - 1 || $c_end >= $size) { 

     header('HTTP/1.1 416 Requested Range Not Satisfiable'); 
     header("Content-Range: bytes $start-$end/$size"); 
     // (?) Echo some info to the client? 
     exit; 
    } 
    $start = $c_start; 
    $end = $c_end; 
    $length = $end - $start + 1; // Calculate new content length 
    fseek($fp, $start); 
    header('HTTP/1.1 206 Partial Content'); 
} 
// Notify the client the byte range we'll be outputting 
header("Content-Range: bytes $start-$end/$size"); 
header("Content-Length: $length"); 

// Start buffered download 
$buffer = 1024 * 8; 
while(!feof($fp) && ($p = ftell($fp)) <= $end) { 

    if ($p + $buffer > $end) { 

     // In case we're only outputtin a chunk, make sure we don't 
     // read past the length 
     $buffer = $end - $p + 1; 
    } 
    set_time_limit(0); // Reset time limit for big files 
    echo fread($fp, $buffer); 
    flush(); // Free up memory. Otherwise large files will trigger PHP's memory limit. 
} 

fclose($fp); 

} 


header('location:../login.php'); 
?> 

回答

1

我用mod_xsendfile這個 https://tn123.org/mod_xsendfile/

讓Apache的處理服務的文件,而不是試圖複製這一切在PHP :)

+0

,而是參考htaccess的建議可能是... – barryhunter

+0

Xsendfile無疑簡化我的代碼並可能增加我的網站的效率。但是,使用xsendfile時,我的原始腳本仍存在兩個相同的問題。也就是說,僞流式傳輸不起作用,思維者只會在拋出錯誤之前通過一次播放視頻。您是否遇到過有關設置的任何問題?任何解決這些問題的方法?絕對是一個有用的建議,雖然和現場關於我使用Apache,所以謝謝。 – Jeff

1

是的,它很容易做到。不需要手動設置這些標題。讓服務器自動執行。

繼承人,我寫了一個視頻流媒體代理工作的腳本 -

ini_set('memory_limit','1024M'); 

set_time_limit(3600); 

ob_start(); 

**// do any user checks here - authentication/ip restriction/max downloads/whatever** 

**// if check fails, return back error message** 

**// if check succeeds, proceed with code below** 

if(isset($_SERVER['HTTP_RANGE'])) 

$opts['http']['header']="Range: ".$_SERVER['HTTP_RANGE']; 

$opts['http']['method']= "HEAD"; 

$conh=stream_context_create($opts); 

$opts['http']['method']= "GET"; 

$cong= stream_context_create($opts); 

$out[]= file_get_contents($real_file_location_path_or_url,false,$conh); 

$out[]= $http_response_header; 

ob_end_clean(); 

array_map("header",$http_response_header); 

readfile($real_file_location_path_or_url,false,$cong); 
當然我不actully你使用Apache知道
相關問題