I am using this below code to stream a movie on my localhost. The movie which is lesser then 2GB works fine but when it comes to movie greater than 2gb then the movie doesn't play.
Please help me what should I do to read larger files.
This is the code I am using for streaming movie
<?php
//Determine file path according to extension
if (!isset($_GET['ext']) || $_GET['ext'] == 'mp4') {
$path ='movies/'.$_GET['movie'];
}
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime = $finfo->file($path);
header('Content-Type: ' . $mime);
$size = filesize($path);
if (isset($_SERVER['HTTP_RANGE'])) {
list($specifier, $value) = explode('=', $_SERVER['HTTP_RANGE']);
if ($specifier != 'bytes') {
header('HTTP/1.1 400 Bad Request');
return;
}
list($from, $to) = explode('-', $value);
if (!$to) {
$to = $size - 1;
}
$fp = fopen($path, 'rb');
if (!$fp) {
header('HTTP/1.1 500 Internal Server Error');
return;
}
header('HTTP/1.1 206 Partial Content');
header('Accept-Ranges: bytes');
header('Content-Length: ' . ($to - $from));
header("Content-Range: bytes {$from}-{$to}/{$size}");
fseek($fp, $from);
while(true){
if(ftell($fp) >= $to){
break;
}
echo fread($fp, 8192);
// Flush do buffer
ob_flush();
flush();
}
}
else {
header('Content-Length: ' . $size);
readfile($path);
}
I have hosted the files on localhost using xammpp as a server.
This could have different reasons.
fopen() reads a file into your RAM. So the limitation seems to come from there.
First, make sure you have min 4GB installed in your system.
If you need more also make sure you dont use a Win 32-Bit system since this only supports up to 4GB ram.
Then you can try to increase the memory_limit of PHP. Open the xampp/php/php.ini file and change the memory_limit to what ever you need OR set the ini value directly in your php file like this
ini_set(”memory_limit”,”16M”);
Hope this helps. For a more specific answer it would be helpful if you have any error message.
Ok, I give up... Something very strange is going on and, after days of messing with this, I have to ask for help. I have a PHP script that serves an MP4 file from outside of the document root. This script works great, except for one very important (to me at least) detail: it will not give me the option to cast the content. On the same server, when I access an MP4 file that IS inside the document root, I load the page and when I click the three dots in the bottom right corner of the Chrome video player, I have the option to Download or Cast to my Chromecast. Using my script, I only have the option to Download, and I REALLLLY need to CAST! I have tweaked this so much that the headers output from either method are all but identical. Here is my code...
<?php
$file=$_GET['file'];
//validate
if($file=="." || $file==".."){$file="";}
$mediaRoot="../../../hostMedia";
$file=$mediaRoot . DIRECTORY_SEPARATOR . $file;
$file=str_replace('\\',"/",$file);
$filesize = filesize($file);
$offset = 0;
$length = $filesize;
// find the requested range
preg_match('/bytes=(\d+)-(\d+)?/', $_SERVER['HTTP_RANGE'], $matches);
$offset = intval($matches[1]);
$length = (($matches[2]) ? intval($matches[2]) : $filesize) - $offset;
// output the right headers for partial content
header('HTTP/1.1 206 Partial Content');
header('Content-Range: bytes ' . $offset . '-' . ($offset + $length-1) . '/' . $filesize);
header('Content-Type: video/mp4');
header('Content-Length: ' . $filesize);
header('Accept-Ranges: bytes');
header('Cache-Control: max-age=0');
// open file for reading
$file = fopen($file, 'r');
// seek to the requested offset, this is 0 if it's not a partial content request
fseek($file, $offset);
// populate $data with all except the last byte of the file
$numBytes=($filesize-1);
$dataLen=0;
while($dataLen<$numBytes){
$lenGrab=($numBytes-$dataLen);
if($lenGrab>(1024*2700)){$lenGrab=(1024*2700);}
$data=fread($file, $lenGrab);
print($data);
$dataLen+=strlen($data);
}
// close file
fclose($file);
?>
A thousand "thank-you"s to whoever solves this one!
UPDATE
Ok, taking #Brian Heward's advice, I have spent countless hours making sure that the headers are ABSOLUTELY IDENTICAL!!! I was so sure it would work, but alas, it still fails to give me the option to cast. Here is my updated PHP...
<?php
session_start();
$accessCode=$_SESSION['accessCode'];
$file=$_GET['file'];
//handle injection
if($file=="." || $file==".."){$file="";}
if($accessCode=="blahblahblah8"){
$mediaRoot="../../../hostMedia";
$file=$mediaRoot . DIRECTORY_SEPARATOR . $file;
$file=str_replace('\\',"/",$file);
$filesize = filesize($file);
$offset = 0;
$length = $filesize;
$lastMod=filemtime($file);
if ( isset($_SERVER['HTTP_RANGE']) ) {
// if the HTTP_RANGE header is set we're dealing with partial content
$partialContent = true;
// find the requested range
preg_match('/bytes=(\d+)-(\d+)?/', $_SERVER['HTTP_RANGE'], $matches);
$offset = intval($matches[1]);
$length = (($matches[2]) ? intval($matches[2]) : $filesize) - $offset;
} else {
$partialContent = false;
}
if ( $partialContent ) {
// output the right headers for partial content
header('HTTP/1.1 206 Partial Content');
header('Content-Range: bytes ' . $offset . '-' . ($offset + $length-1) . '/' . $filesize);
}else{
header('HTTP/1.1 200 OK');
}
// output the regular HTTP headers
header('Content-Type: video/mp4');
header('Content-Length: ' . $length);
header('Accept-Ranges: bytes');
header('ETag: "3410d79f-576de84c004aa"');
header('Last-Modified: '.gmdate('D, d M Y H:i:s \G\M\T', $lastMod));
// don't forget to send the data too
$file = fopen($file, 'r');
// seek to the requested offset, this is 0 if it's not a partial content request
fseek($file, $offset);
//populate $data with all except the last byte of the file
$numBytes=($length);
$dataLen=0;
while($dataLen<$numBytes){
$lenGrab=($numBytes-$dataLen);
if($lenGrab>(1024*2700)){$lenGrab=(1024*2700);}
$data=fread($file, $lenGrab);
print($data);
$dataLen+=strlen($data);
}
fclose($file);
}else{
echo "You are not authorized to view this media.";
}
?>
If someone can get this thing to work, you are seriously a superhero!
FINAL UPDATE (for now...)
Well, after many, many hours of frustration, I had to abandon the approach and try something different. Luckily, there are usually more than one way to accomplish something, and I have found another way. I am hosting the .mp4 files inside the doc root in a folder protected using HTTP Basic Auth. Very similar to what I was trying to achieve and it is working for me. Thanks for your advice and direction!
Your headers are "all but identical" and there is the problem. Make them identical :P
Use the developer tools on your browser, (F12) and check the network headers each request is making. The most likely causes are the following lines I used on a similar project and you seem to be missing:
header('Content-Description: File Transfer');
header('Content-Disposition: inline; filename=' . basename($file));
alternately it might want
header('Content-Disposition: attachment; filename=' . basename($file));
This script might be what you're looking for, it handles video serving via PHP really well, Source
<?php
// disable zlib so that progress bar of player shows up correctly
if(ini_get('zlib.output_compression')) {
ini_set('zlib.output_compression', 'Off');
}
$folder = '.';
$filename = 'video.mp4';
$path = $folder.'/'.$filename;
// from: http://l...content-available-to-author-only...n.net/post/stream-videos-php/
if (file_exists($path)) {
// Clears the cache and prevent unwanted output
ob_clean();
$mime = "video/mp4"; // The MIME type of the file, this should be replaced with your own.
$size = filesize($path); // The size of the file
// Send the content type header
header('Content-type: ' . $mime);
// Check if it's a HTTP range request
if(isset($_SERVER['HTTP_RANGE'])){
// Parse the range header to get the byte offset
$ranges = array_map(
'intval', // Parse the parts into integer
explode(
'-', // The range separator
substr($_SERVER['HTTP_RANGE'], 6) // Skip the `bytes=` part of the header
)
);
// If the last range param is empty, it means the EOF (End of File)
if(!$ranges[1]){
$ranges[1] = $size - 1;
}
// Send the appropriate headers
header('HTTP/1.1 206 Partial Content');
header('Accept-Ranges: bytes');
header('Content-Length: ' . ($ranges[1] - $ranges[0])); // The size of the range
// Send the ranges we offered
header(
sprintf(
'Content-Range: bytes %d-%d/%d', // The header format
$ranges[0], // The start range
$ranges[1], // The end range
$size // Total size of the file
)
);
// It's time to output the file
$f = fopen($path, 'rb'); // Open the file in binary mode
$chunkSize = 8192; // The size of each chunk to output
// Seek to the requested start range
fseek($f, $ranges[0]);
// Start outputting the data
while(true){
// Check if we have outputted all the data requested
if(ftell($f) >= $ranges[1]){
break;
}
// Output the data
echo fread($f, $chunkSize);
// Flush the buffer immediately
#ob_flush();
flush();
}
}
else {
// It's not a range request, output the file anyway
header('Content-Length: ' . $size);
// Read the file
#readfile($path);
// and flush the buffer
#ob_flush();
flush();
}
}
die();
?>
I need to log total downloads of an specific file. Download function is working fine, but can't define if user canceled (clicking "cancel" on browser dialog) or if connection was aborted latter.
I understand it's not simple to know when a file download was finished, so I'm trying to get this by two ways. None works:
Get total bytes sent, latter I will compare it with total file size: this way $bytes_sent var always is set with total file size, no matter if user click cancel button of download dialog or if cancel download process latter.
Trigger connection_aborted() function: Have not found the way this function happen and define my session var...
(I'm not shure if the fact Im working with sessions is relevant).
I appreciate your help :)
<?php
if(is_file($filepath)){
$handle = fopen($filepath, "r");
header("Content-Type: $mime_type");
header("Content-Length: ". filesize($filepath).";");
header("Content-disposition: attachment; filename=" . $name);
while(!feof($handle)){
ignore_user_abort(true);
set_time_limit(0);
$data = fread($handle, filesize($filepath));
print $data;
$_SESSION['download'] = 'Successful download';
//Always is set as total file lenght, even when cancel a large file download before it finish:
bytes_sent = ftell($handle);
flush();
ob_flush();
//Can't trigger connection aborted, in any case:
if(connection_aborted()){
$_SESSION['download'] = 'Canceled download';
}
}
}
PHP Version 5.3.29
You need to read the file in small chunks, rather than reading it all in at once.
$chunk_size = 1000;
ignore_user_abort();
$canceled = false;
while ($chunk = fread($handle, $chunk_size)) {
print $chunk;
ob_flush();
$bytes_sent += strlen($chunk);
if (connection_aborted()) {
$canceled = true;
break;
}
}
$_SESSION['download'] = $canceled ? "Download canceled" : "Download successful";
I've got problem streaming a mp4 file throught php.
The downloading of the files hangs on 1MB. You can see it here:
I've tried many headers and read many threads, but lots of them are unresolved or didnt help me, like this one: MP4 plays when accessed directly, but not when read through PHP, on iOS . Any help please?
There is my code:
<?
$root=getenv("DOCUMENT_ROOT");
$file = "$root/".$_GET['get'];
header("Content-Type: video/mp4");
//header("Content-Type: application/octet-stream"); // downloads the file
$start=0;
$size = filesize($file);
$length = $size;
$end = $size - 1;
//header('HTTP/1.1 206 Partial Content');
header("Accept-Ranges: $start-$end");
header("Content-Range: bytes $start-$end/$size");
header("Content-Length: $length");
$data = fopen("$file", "r");
fseek($data, $start);
$bytesleft = $size-$start + 1;
$buffer = 1024 * 256; // 256kb
while(!feof($data)){
if($bytesleft > $buffer){
echo fread($data, $buffer);
flush();
}else{
echo fread($data, $bytesleft);
flush();
}
//sleep(1); // Speedlimit = one buffer per second
}
fclose($data);
exit;
?>
Thanks in advance
Nevermind. I downloaded the file from the server and compared it with original stored on server in hex.
The downloaded one had one more byte on begining: 0A and that was the problem. Everything works now.
I have some php code that reads the first file of a remote zip file containing mp3s. I know that in the zip file format, each file has a compressed size in its header. However, when I use fread to that number after going through the header, it only goes to about 5454 characters. I have made a fix, but it is very slow.
$comp = "";
while(strlen($comp) <= $cpsz){
/*$cpsz is the compressed size*/
$comp .= fread($fh, 1);
}
header("Accept-Ranges:bytes");
header("Connection: Keep-Alive");
header("Content-Type:audio/mpeg");
header("Content-Length:" . $ucps);
header("Content-Range:bytes 0-" . ($ucps-1) . "/" . $ucps);
header("ETag: xyz");
http_response_code (206);
$file = gzinflate($comp);
echo $file;
Is there anyway to make this more efficient than reading one byte at a time?