I have the following scenario: You can download from our server some files. If you're a "normal" user, you have a limited bandwidth for example 500kbits. If you're a premium user, you haven't a limit of your bandwidth and can download as fast a possible. But how I can realize this? How does this uploaded & co?
-
Good question, needs good answer. – Timtech Dec 17 '14 at 12:09
-
possible duplicate of [Limit download speed using PHP](http://stackoverflow.com/questions/4002106/limit-download-speed-using-php) – Ole Haugset Dec 17 '14 at 12:11
-
@OleHaugset Not the same question, answers there do not apply to this question. – Alfabravo Dec 17 '14 at 12:27
-
@Alfabravo: can you indicate why you think that? Looks like a direct dup to me. Also, these [similar questions and answers](https://stackoverflow.com/search?q=php+bandwidth+throttle) would be worth browsing through. – halfer Dec 17 '14 at 12:29
-
1@halfer: I thought about marking this question as a duplicate, too, and link to the same question as you did. However, that question only focuses on one of the many ways to limit the download rate, and IMO, it's not the best way. That's why I decided to post an answer, explaining the solution in that dupe, and suggest alternatives that are, IMHO, better. Just so you know: that's why I'm reluctant to mark this question as a dupe, especially since I have the 1-vote-close privilege – Elias Van Ootegem Dec 17 '14 at 13:03
-
@Elias, good thoughts. I'm just a bit wary when a very short question featuring no prior research attracts two comments defending it, and - ha! 7 upvotes. Hmm mumble mumble... – halfer Dec 17 '14 at 13:06
-
1@halfer: I completely agree with you. Just commented to let you know why I didn't vote to close, not to _"defend"_ the question because you're right: there's no evidence of any research efforts on the OP's part – Elias Van Ootegem Dec 17 '14 at 13:32
-
@halfer The other question focus on keeping conn alive with no reference to actually limiting DL speed. This one focus on the code to limit DL speed. Someone looking for the answer posted here won't find it in the other question. PS. I'm not acquinted with OP, if you still are suspicious about the "short question attracting attention". – Alfabravo Dec 17 '14 at 17:14
3 Answers
Note: you can do this with PHP, but I would recommend you let the server itself handle throttling. The first part of this answer deals with what your options are if you want to cap the download speed with PHP alone, but below you'll find a couple of links where you'll find how to manage download limits using the server.
There is a PECL extension that makes this a rather trivial task, called pecl_http, which contains the function http_throttle
. The docs contain a simple example of how to do this already. This extension also contains a HttpResponse
class, which isn't well documented ATM, but I suspect playing around with its setThrottleDelay
and setBufferSize
methods should produce the desired result (throttle delay => 0.001, buffer size 20 == ~20Kb/sec). From the looks of things, this ought to work:
$download = new HttpResponse();
$download->setFile('yourFile.ext');
$download->setBufferSize(20);
$download->setThrottleDelay(.001);
//set headers using either the corresponding methods:
$download->setContentType('application/octet-stream');
//or the setHeader method
$download->setHeader('Content-Length', filesize('yourFile.ext'));
$download->send();
If you can't/don't want to install that, you can write a simple loop, though:
$file = array(
'fname' => 'yourFile.ext',
'size' => filesize('yourFile.ext')
);
header('Content-Type: application/octet-stream');
header('Content-Description: file transfer');
header(
sprintf(
'Content-Disposition: attachment; filename="%s"',
$file['fname']
)
);
header('Content-Length: '. $file['size']);
$open = fopen($file['fname'], "rb");
//handle error if (!$fh)
while($chunk = fread($fh, 2048))//read 2Kb
{
echo $chunk;
usleep(100);//wait 1/10th of a second
}
Of course, don't buffer the output if you do this :), and it might be best to add a set_time_limit(0);
statement, too. If the file is big, it's quite likely your script will get killed mid-way through the download, because it hits the max execution time.
Another (and probably preferable) approach would be to limit the download speed through the server configuration:
- using NGINX
- using Apache2
- using MS IIS (either install the bitrate throttling module, or set the max bandwith)
I've never limited the download rates myself, but looking at the links, I think it's fair to say that nginx is the easiest by far:
location ^~ /downloadable/ {
limit_rate_after 0m;
limit_rate 20k;
}
This makes the rate limit kick in immediately, and sets it to 20k. Details can be found on the nginx wiki.
As far as apache is concerned, it's not that much harder, but it'll require you to enable the ratelimit module
LoadModule ratelimit_module modules/mod_ratelimit.so
Then, it's a simple matter of telling apache which files should be throttled:
<IfModule mod_ratelimit.c>
<Location /downloadable>
SetOutputFilter RATE_LIMIT
SetEnv rate-limit 20
</Location>
</IfModule>

- 74,482
- 9
- 111
- 149
You can use http_throttle
from the pecl_http
PHP extension:
<?php
// ~ 20 kbyte/s
# http_throttle(1, 20000);
# http_throttle(0.5, 10000);
if (!$premium_user) {
http_throttle(0.1, 2000);
}
http_send_file('document.pdf');
?>
(The above is based on an example from http://php.net/manual/en/function.http-throttle.php.)
If your server API doesn't allow http_throttle
create two distinct, unguessable URLs for your premium and nonpremium users and refer to your HTTP server's documentation for how to throttle one of them. See https://serverfault.com/questions/179646/nginx-throttle-requests-to-prevent-abuse for an example for Nginx. The latter has the benefit of allowing you to sidestep problems like downloads being terminated early due to PHP killing your script.
There's this PHP user land library bandwidth-throttle/bandwidth-throttle
use bandwidthThrottle\BandwidthThrottle;
$in = fopen(__DIR__ . "/resources/video.mpg", "r");
$out = fopen("php://output", "w");
$throttle = new BandwidthThrottle();
$throttle->setRate(500, BandwidthThrottle::KIBIBYTES); // Set limit to 500KiB/s
$throttle->throttle($out);
stream_copy_to_stream($in, $out);

- 7,738
- 2
- 38
- 67