Throttling download speed in PHP - php

I have tried the basic ones found in a Google search and even tried to write one myself, however i keep getting a problem with it. It seems to download the content server-side or something and then push it to the user, which will already have been downloaded. It will open the download page and take around 10 seconds to download and then give the file to the user in full, which makes it look like its not downloading.
I was wondering if there are any classes that have been written to throttle download speeds, or how i can fix this problem.
I have this currently;
header("Content-type: application/force-download");
header("Content-Transfer-Encoding: Binary");
header("Content-length: ".filesize("uploads/$filename"));
header("Content-disposition: attachment; filename=\"$origname");
readfile("uploads/$filename");
Thanks!

#set_time_limit(0); // don't abort if it takes to long
header("Content-type: application/force-download");
header("Content-Transfer-Encoding: Binary");
header("Content-length: ".filesize("uploads/".$filename));
header('Content-disposition: attachment; filename="'.$origname.'"');
$perSecond = 5; // 5 bytes per second
$file = fopen("uploads/".$filename, 'r');
while(!feof($file)) {
echo fread($file, $perSecond);
flush();
sleep(1);
}
This will send a file with throttled download speed to the user. It works basically like this:
Open a file
loop until we are at the end
echo X bytes
flush the output to the User
sleep for one second.

You might find my alpha-stage Bandwidth project of interest. Probably needs a bit more work, but there's plenty of interesting stuff already. I don't think it has a F/OSS license yet; ping me if you want me to give it one!

I was wondering if there are any classes that have been written to throttle download speeds
Now there is: bandwidth-throttle/bandwidth-throttle
use bandwidthThrottle\BandwidthThrottle;
$in = fopen(__DIR__ . "/resources/video.mpg", "r");
$out = fopen("php://output", "w");
$throttle = new BandwidthThrottle();
$throttle->setRate(100, BandwidthThrottle::KIBIBYTES); // Set limit to 100KiB/s
$throttle->throttle($out);
stream_copy_to_stream($in, $out);

Related

PHP: output file without getting it into memory [duplicate]

I want to serve an existing file to the browser in PHP.
I've seen examples about image/jpeg but that function seems to save a file to disk and you have to create a right sized image object first (or I just don't understand it :))
In asp.net I do it by reading the file in a byte array and then call context.Response.BinaryWrite(bytearray), so I'm looking for something similar in PHP.
Michel
There is fpassthru() that should do exactly what you need. See the manual entry to read about the following example:
<?php
// open the file in a binary mode
$name = './img/ok.png';
$fp = fopen($name, 'rb');
// send the right headers
header("Content-Type: image/png");
header("Content-Length: " . filesize($name));
// dump the picture and stop the script
fpassthru($fp);
exit;
?>
See here for all of PHP's filesystem functions.
If it's a binary file you want to offer for download, you probably also want to send the right headers so the "Save as.." dialog pops up. See the 1st answer to this question for a good example on what headers to send.
I use this
if (file_exists($file)) {
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename='.basename($file));
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
ob_clean();
flush();
readfile($file);
exit;
}
I use readfile() ( http://www.php.net/readfile )...
But you have to make sure you set the right "Content-Type" with header() so the browser knows what to do with the file.
You can also force the browser to download the file instead of trying to use a plug-in to display it (like for PDFs), I always found this to look a bit "hacky", but it is explained at the above link.
This should get you started:
http://de.php.net/manual/en/function.readfile.php
Edit: If your web server supports it, using
header('X-Sendfile: ' . $filename);
where file name contains a local path like
/var/www/www.example.org/downloads/example.zip
is faster than readfile().
(usual security considerations for using header() apply)
For both my website and websites I create for clients I use a PHP script that I found a long time ago.
It can be found here: http://www.zubrag.com/scripts/download.php
I use a slightly modified version of it to allow me to obfuscate the file system structure (which it does by default) in addition to not allowing hot linking (default) and I added some additional tracking features, such as referrer, IP (default), and other such data that I might need should something come up.
Hope this helps.
Following will initiate XML file output
$fp = fopen($file_name, 'rb');
// Set the header
header("Content-Type: text/xml");
header("Content-Length: " . filesize($file_name));
header('Content-Disposition: attachment; filename="'.$file_name.'"');
fpassthru($fp);
exit;
The 'Content-Disposition: attachment' is pretty common and is used by sites like Facebook to set the right header

PHP Download Problem: Not Successful w/ Slow Connection Speeds

I've been stuck on this problem for a few days, and have yet to find a solution that fixes the problem I'm having.
What I'm Trying To Do:
I'm attempting to use PHP to download PDFs, and the code works very well for files that can download within about a minute and a half. On my home wifi, I'm able to download a 159MB file within 10 seconds, and it works every time. But when I limit the internet speed to "Fast 3G" (around 170KB/s, in order to simulate slower office speeds), the download fails. And nearly every time, it does so exactly 3 minutes and 24 seconds into the download process, but occasionally it is a lower time of 1 minute and 57 seconds.
What I've Tried:
I've tweaked the php.ini file (setting max_execution_time = 0, and memory_limit at higher intervals than the originally configured 128M)
I've tried other download methods that seem to "chunk" the larger PDFs. This has been mostly unsuccessful. In one instance the download would complete, but there would be an error when trying to open the PDF. According to the poster of this solution, it was only a valid solution for UTF-8 encoded files, and I found the one's I'm dealing with to be UTF-16. (I believe it was some kind of incompatibility with the print() function.
I've made sure the file can download if using a direct link in the URL. It has no problems this way, but it was only done for testing, and cannot be a permanent solution because the PDFs I'm dealing with contain sensitive information. So based off of this result, I was at least able to narrow down the problem to be PHP related and not IIS.
Here's the current code I'm using
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header("Pragma: public");
header("Expires: 0");
header("Cache-Control:must-revalidate, post-check=0, pre-check=0");
header("Content-Type: application/force-download");
header("Content-Type: application/download");
header('Content-Disposition: attachment; filename="'.basename($file).'"');
header("Content-Transfer-Encoding: binary ");
header('Content-Length: ' . filesize($file));
//$file is a full path to the PDF
while(ob_get_level()) {
ob_end_clean();
}
readfile($file);
flush();
exit;
/*I realize it may be off, but it is at least working for quicker load
times as it currently is, so I'm leaving it alone for now*/
I tried to include any information that seemed relevant, but if any additional information would be useful please let me know! I will also be sure to include the current code that is handling the download process that I mentioned at the top of the post.
Instead of
readfile($file);
flush();
I would try
$handle = fopen($file, 'r');
while (!feof($handle)) {
echo fread($handle, 8192);
flush();
}
fclose($handle);
you may need to adjust the above to handle proper encoding, but that will depend on your environment

Returning a local BIG PDF to be viewed with PHP isn't working properly

I figured out how to return the PDF correctly, however it takes 5 - 20 seconds (depending on file size) for Google Chrome/Microsoft Edge/Internet Explorer to show a progress bar.
$file = 'http://foobar.com/data/users/1/uploads/2342343/signed/protected.pdf';
$filename = 'protected';
$headers = get_headers($file, 1);
$fsize = $headers['Content-Length'];
header('Content-type: application/pdf');
header('Content-Disposition: inline; filename="' . $filename . '"');
header('Content-Transfer-Encoding: binary');
header('Content-Length: ' . $fsize);
header('Accept-Ranges: bytes');
#readfile($file);
This is taking way to long for it to actually display a result because the loading doesn't fire fast enough. What am I missing? Am I doing something wrong to cause the progress bar to not immediately show to start loading the PDF? Is the get_headers actually downloading the file first?
Or what is the best way to return a BIG PDF in the fastest way possible?
I think you should read the file in a stream fashion, flushing the content parts to the client.
I did some code to read in a stream fashion these days, but I was using the OCI-Lob::read, because my PDF was stored in an Oracle database. I think your file may be stored in a different way, so you need a different implementation. In my case, I read the file contents 1MB each time. I was not working with flushing content to the client.
I'm not that expert in PHP, but I think you could take a look in the flush function to accomplish the loading progress.

Asking user to download file in PHP

I am trying to get the browser to prompt the user to download a file. However, after having tried several methods from stack overflow and around the Internet, for some reason all are silently failing. Is it the case that this just isn't possible in modern browsers?
I'm simply wanting the user to download a text (.txt) file from the server. I've tried this code below (and more) to no avail:
header('Content-disposition: attachment; filename=newfile.txt');
header('Content-type: text/plain');
readfile('newfile.txt');
.
header("Content-Type: application/octet-stream");
$file = $_GET["file"] .".txt";
header("Content-Disposition: attachment; filename=" . urlencode($file));
header("Content-Type: application/octet-stream");
header("Content-Type: application/download");
header("Content-Description: File Transfer");
header("Content-Length: " . filesize($file));
flush(); // this doesn't really matter.
$fp = fopen($file, "r");
while (!feof($fp))
{
echo fread($fp, 65536);
flush(); // this is essential for large downloads
}
fclose($fp);
I have tried the examples from PHP.NET (none of which are working for me):
http://php.net/manual/en/function.readfile.php
I have the correct permissions set, the file exists and is_readable. I'm now left scratching my head as to why this isn't working. Any help would be great.
I have one solution for you.
Lets assume download.php is the file that downloads the file.
So when the user clicks on the link to download show a confirm dialog, if the user selects yes then re direct the user to download.php or else download will not occur some browsers like chrome starts the download without asking users if they like to download a file or not.

PHP output file on disk to browser

I want to serve an existing file to the browser in PHP.
I've seen examples about image/jpeg but that function seems to save a file to disk and you have to create a right sized image object first (or I just don't understand it :))
In asp.net I do it by reading the file in a byte array and then call context.Response.BinaryWrite(bytearray), so I'm looking for something similar in PHP.
Michel
There is fpassthru() that should do exactly what you need. See the manual entry to read about the following example:
<?php
// open the file in a binary mode
$name = './img/ok.png';
$fp = fopen($name, 'rb');
// send the right headers
header("Content-Type: image/png");
header("Content-Length: " . filesize($name));
// dump the picture and stop the script
fpassthru($fp);
exit;
?>
See here for all of PHP's filesystem functions.
If it's a binary file you want to offer for download, you probably also want to send the right headers so the "Save as.." dialog pops up. See the 1st answer to this question for a good example on what headers to send.
I use this
if (file_exists($file)) {
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename='.basename($file));
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
ob_clean();
flush();
readfile($file);
exit;
}
I use readfile() ( http://www.php.net/readfile )...
But you have to make sure you set the right "Content-Type" with header() so the browser knows what to do with the file.
You can also force the browser to download the file instead of trying to use a plug-in to display it (like for PDFs), I always found this to look a bit "hacky", but it is explained at the above link.
This should get you started:
http://de.php.net/manual/en/function.readfile.php
Edit: If your web server supports it, using
header('X-Sendfile: ' . $filename);
where file name contains a local path like
/var/www/www.example.org/downloads/example.zip
is faster than readfile().
(usual security considerations for using header() apply)
For both my website and websites I create for clients I use a PHP script that I found a long time ago.
It can be found here: http://www.zubrag.com/scripts/download.php
I use a slightly modified version of it to allow me to obfuscate the file system structure (which it does by default) in addition to not allowing hot linking (default) and I added some additional tracking features, such as referrer, IP (default), and other such data that I might need should something come up.
Hope this helps.
Following will initiate XML file output
$fp = fopen($file_name, 'rb');
// Set the header
header("Content-Type: text/xml");
header("Content-Length: " . filesize($file_name));
header('Content-Disposition: attachment; filename="'.$file_name.'"');
fpassthru($fp);
exit;
The 'Content-Disposition: attachment' is pretty common and is used by sites like Facebook to set the right header

Categories