Basically, what I want to do is to check how much of a file my webserver has sent to a client, when the client is downloading one. Is this even possible?
Does apache provide any module/extension that would help me accomplish my task?
I use a linux distro, apache2 and php5. Regards.
Browser provides this functionality if file has correct "Content-length" header set. Why do you want to implement this in your page?
Solved it.
I simply open the file with PHP that I want to send to the client.
$fh = fopen($filePath, 'r');
Then I calculate 60% of the filesize by writing
$fileSize = filesize($filePath);
$sizeFirst = floor(($fileSize / 100) * 60);
Now the $sizeFirst variable contains the length of the first 60% of the file, in a numeric value.
To calculate the rest 40% I use:
$sizeLast = $fileSize - $sizeFirst;
Now I can write out the first 60%, do my action, and then write outh the rest 40%.
$dataFirst = fread($fh, $sizeFirst);
echo($dataDirst);
// Do my action here.
$dataSecond = fread($fh, $sizeSecond);
echo($dataSecond);
exit();
I need to set the header(); before writing out this, the Content-length, Content-type and Content-Disposition must be set in order to send a valid header and filecontent to the client.
Hope it helps someone.
Related
I wanted to create a .php file, that streams a video!
Now, the problem is, that it works, only if i use a normal readfile(), but then, you can not go back and forward in the video, so i searched on google, to find this code:
(basically, the HTTP_RANGE does not work, NEVER, i do not know why, when testing it, it always fires my die("lol?");, so it clearly does not support it for some reason)
(the die() function is left there on purpose, it will be taken out if it would work..)
(note that i changed "$size = filesize($file);" to "$size = filesize(".".$file);", because someone mentioned that this is required, and "filesize($file);" does not work for me anyways, it always fires an error)!
(and, the $file, shows the actual path for my file, nothing replaced, its how it looks in the original php of me!)
<?php
// Clears the cache and prevent unwanted output
ob_clean();
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
apache_setenv('no-gzip', 1);
ini_set('zlib.output_compression', 'Off');
$file = "/cdn4-e663/zw4su8jiy8skgvizihsjehj/2038tkusi9u848sui7zh/2q3z6hjk97ujduz/a1-cdn/9zw35jbmhkk47wi63uu7.mp4"; // The media file's location
$mime = "application/octet-stream"; // The MIME type of the file, this should be replaced with your own.
$size = filesize(".".$file); // 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($file, '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]);
die("working?");
// 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 {
die("lol?");
header('Content-Length: ' . $size);
// Read the file
readfile($file);
// and flush the buffer
ob_flush();
flush();
}
?>
so, the die("lol?"); was added by me to see if the
if(isset($_SERVER['HTTP_RANGE'])){
/*function fires or not, and no, as it seems it returns FALSE every time..8/
}
so i wanted to ask you all, how can i fix this? i really want to use php to stream my video, because of security reasons, and because i like it, i already use this methode with images but its a different code(and working)!
I am using Apache 2.4 (Windows 10 - 64bit PC) with the latest version of PHP7, but it seems that apache does not support HTTP_RANGE? am i missing something, is there something i need to enable inside either the php.ini or the httpd.conf??
Thank you in advance, i hope someone can tells me what to do, because i really am stuck here, and i tried ALL examples of mp4 video streaming i could find on google, and none worked for me :/
There are 2 parts to this:
The request made by the browser/client. This must send appropriate request headers.
The response given by your server. This is done by your PHP script and must also send the appropriate response headers
When you try and stream your video (or whatever the content is) open the Network tab in your browser.
Look at the Request Headers (in Chrome this is under the Network tab). I've posted a screenshot below. Note that in the request there is a Range: parameter. If this is not present in the request, you'll have problems. This is what tells the PHP script on the server that you are doing a range request in the first place. If the server does not see this header in the request then it will just bypass the if statement and go into the die.
Note that the Range: request header is not normally included in requests by default, so unless you are specifying this, it will never work. If you don't see it in the Request Headers on your Network tab, it is not present, and you need to fix that.
You may also want to examine the response headers - which are totally different from the request headers. Again, these can be seen in the Network tab in your browser. See below for the appropriate headers that must be set:
Going back to the original question, none of it has anything to do with the response (which is what you were describing). The initial problem you are having is all to do with how you're making the request and the fact it does not contain a Range: header, when it must do so.
I am developing an embedded device which has a simple miro-controller with limited memory. This device will request a file from a server by sending a HTTP (or HTTPS) GET method request to the server. There will be a PHP script which in the server responsible to send the file. Now the PHP script will only send the file continuously to the embedded device. However as the embedded device is not fast enough and do not have enough memory to store the whole file before processing it. I want the PHP script to only sending a chunk of the file in each HTTP GET request. I think it is good that the size of the chunk is determined by the variable in the GET request. And in each chunk it will add a header describing the size, the sequence number, and CRC check of that chunk.
I am a newbie on PHP script. Could you help to guild me to write the PHP script? An example would be really appreciated.
Thank you very much.
I think that your script PHP could read the file and take the chunk you want:
$filename = "YOURFILE.txt";
$chunk_length = 1024; // 1024 chars will be sent
$sequence_number = $_GET['sequence'];
if ($sequence_number>0){
$position = $sequence_number * $chunk_length;
}
else {
$position = 0;
}
$content = file_get_contents($filename);
$data = substr($content, $position, $chunk_length);
header('size:'.strlen($data));
header('sequence_number:'.sequence_number);
header('CRC:'.crc32($data));
echo $data;
I'm currently looking into a way of showing the file download status on a page.
I know this isnt needed since the user usually has a download status in the browser, but I would like to keep the user on the page he is downloading from, as long as the download is lasting. To do that, the download status should match the status the file actually has (not a fake prograss bar). Maybe it will also display the speed the user is downloading at, and estimate the time it will take, depending on the current download rate.
Can this be done using PHP and Javascript? Or does it realy require Flash or Java?
Should not somewhere on the Server be an information about who is downloading what at what speed and how much?
Thank you for your help in advance.
Not really possible cross-browser, but have a look into http://markmail.org/message/kmrpk7w3h56tidxs#query:jquery%20ajax%20download%20progress+page:1+mid:kmrpk7w3h56tidxs+state:results for a pretty close effort. IE (as usual) is the main culprit for not playing ball.
You can do it with two seperate php files, first file for downloading process.
Like as:
$strtTime=time();
$download_rate=120; //downloading rate
$fp = fopen($real, "r");
flush();// Flush headers
while (!feof($fp)) {
$downloaded=round($download_rate * 1024);
echo fread($fp,$downloaded );
ob_flush();
flush();
if (connection_aborted ()) {
// unlink("yourtempFile.txt" ;
exit;
}
$totalDw +=$downloaded;
// file_put_contents("yourtempFile.txt", "downloaded: $totalDw ; StartTime:$strtTime");
sleep(1);
}
fclose($fp);
// unlink("yourtempFile.txt") ;
Second file would be used for reading yourtempFile.txt by Ajax continusly. Using Sessions and Cookies wouldn't be used because of starting print.
I'm finding it difficult to phrase this question correctly, let me try to explain our problem...
We have an intranet running on Ubunutu box with Apache2/PHP 5.2.4. We have a bit of PHP code that reads a file from a directory that is not publically accessible and output it to the screen (code below):
$file_path = '/home/path/to/filename.gif';
if(file_exists($file_path)){
$output = FALSE;
//File Information
$path_parts = pathinfo($file_path);
$file_size = filesize($file_path);
$file_ext = (isset($path_parts['extension'])) ? strtolower($path_parts['extension']) : null;
$file_name = $path_parts['basename'];
//Sets up the headers
if($file_size > 0){
header('Content-Length: ' .$file_size);
}
header('Content-Disposition: attachment; filename="'.$file_name.'"');
header('Content-Type: application/octet-stream');
//Reads the File
if($file_size > 0){
$handle = fopen($file_path, "r");
$output = fread($handle, $file_size);
fclose($handle);
}
//Outputs the File
echo $output;
}
Inside our network when, browsing to the page that uses this code, the file is downloaded perfectly and quickly...
However, when accessing this page via our Cisco ASA/Proxy/VPN (not sure what to call it) this code locks up the browser, but does eventually download the file...
After a bit of experimenting, after taking out the headers and just echoing the contents of the file to the browser, it prints no problem. However as soon as I add the lines with the headers back into the code it causes the hanging again, but only when accessed via this box..
Anybody come across this problem before or have any idea what we can try to move forward?
Thanks for any advice...
Have you tried eliminating the content-size header entirely? The proxy may be taking that as a firm promise and if the data you're sending ends up being a different size, the proxy may wait for those last few "missing" bytes to show up.
Just as an aside, you should use [readfile()][1] instead of the fopen()/fread()/echo construct you have now.
As it stands now, you're slurping the contents of the entire file into memory and then echoing out. For large files and multiple requests, you'll kill the server with memory starvation. readfile will automatically stream the file in smaller chunks so that memory usage is minimal.
Your proxy obviously have problems with the Content-Type: application/octet-stream. Try setting it to the real MIME-type of each file. You can use the Fileinfo module to find out which MIME-type a certain file is, like this:
//You may need to specify the location of your system's magic file
//See http://php.net/finfo_open for more info
$finfo = new finfo(FILEINFO_MIME);
$mimetype = $finfo->file($file_path);
I have a function that will be passed in a link. The link is to a remote image. I thought I could just use the extension of the file in the URL to determine the type of image but some URLs won't have extensions in the URL. They probably just push headers to the browser and therefore I do not have an extension to parse from the URL.
How can I test if the URL has an extension and if not then read the headers to determine the file type?
Am I over complicating things here? Is there an easier way to do this? I am making use of Codeigniter maybe there is something already built in to do this?
All I really want to do is download an
image from a URL with the correct
extension.
This is what I have so far.
function get_image($image_link){
$remoteFile = $image_link;
$ext = ''; //some URLs might not have an extension
$file = fopen($remoteFile, "r");
if (!$file) {
return false;
}else{
$line = '';
while (!feof ($file)) {
$line .= fgets ($file, 4096);
}
$file_name = time().$ext;
file_put_contents($file_name, $line);
}
fclose($file);
}
Thanks all for any help
You never want to switch on the file extension. For example, not all ASCII text files will have ".txt", and then there's always the fun ".jpg" versus ".jpeg". Instead, switch on the Content-Type header that the web server responds with. (See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17).
Some web servers will also respect the Accept header (see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1). So, if you know you only want PNG image files, then send Accept: image/png. If the server respects it, then its response will be much smaller than sending the whole unwanted file over the network connection. However, because not all web servers do this well (if at all), make sure you still switch on the Content-Type response header. It's just a bandwidth saving thing.
You could also send Accept: image/* to get any type of image.
Cheers.
You should always use the Content-Type response header to determine the type of data received. From that you can set the correct extension. URLs do not have extensions, and you should not rely on anything after a period being such.
You won't be able to read headers using fsock/fread or even file_get_contents. That's all hidden away in the background and discarded when the retrieval finishes. You'll have to use the CURL functions and set up a proper HTTP GET session using that, from which you can retrieve full details of the transfer.