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