0

I have a web application written in PHP and Yii that lets users upload images and videos using swfupload. There are two types of users: premium and regular.

I want to limit the upload speed for regular users but I cannot find any way that is not global.

Can the upload speed be limited per user from PHP, Yii or swfupload?

Virgiliu
  • 3,068
  • 6
  • 32
  • 55

2 Answers2

0

There is the stream php://input which…

is not available with enctype="multipart/form-data".

If you don't have that restriction you could register a stream filter and do some token bucket on it, e.g. with bandwidth-throttle/bandwidth-throttle

use bandwidthThrottle\BandwidthThrottle;

$in  = fopen("php://input", "r");
$out = fopen(__DIR__ . "/upload.txt", "w");

$throttle = new BandwidthThrottle();
$throttle->setRate(100, BandwidthThrottle::KIBIBYTES); // Set limit to 100KiB/s
$throttle->throttle($in);

stream_copy_to_stream($in, $out);
Markus Malkusch
  • 7,738
  • 2
  • 38
  • 67
-1

Here is a speed limit solution for download. You can use same for upload. .i.e.read the file into smaller chunk and then upload each chunk with sleep :)

Let’s assume we want to limit the bandwidth for images found in /images/ folder.

First, we need a bandwidth php script, in which we setup the limit of the downloading speed. We achieve that by reading small packets from the file requested, with a timeout between readings:

<?php
//Enter the bandwidth here
$bandwidth = '32'; // KB/s
//For security reason, we will add here all the pattern that we allow for the filename
//Change this to your own needs
$allowed_file_patterns = array('/images\/(\w+)\.(jpg|gif|jpeg|png)/i');
 
function getMimeType($file_path)
{
    $mtype = '';
    if (function_exists('mime_content_type')){
        $mtype = mime_content_type($file_path);
    }
    elseif (function_exists('finfo_file')){
        $finfo = finfo_open(FILEINFO_MIME);
        $mtype = finfo_file($finfo, $file_path);
        finfo_close($finfo);
    } elseif (function_exists('getimagesize')){
        $finfo = @getimagesize($file_path);
        //var_dump($finfo);
        $mtype = !empty($finfo['mime'])?$finfo['mime']:'';
    }
    if ($mtype == ''){
             $mtype = "application/force-download";
    }
    return $mtype;
}
$accepted_pattern = false;
foreach ($allowed_file_patterns as $pattern){
    if (preg_match($pattern,$_GET['file'])){
        $accepted_pattern = true;
    }
}
if (!$accepted_pattern){
    //Stop the script if is not a valid access
    header("HTTP/1.1 403 Forbidden");
    echo 'Forbidden request';
    exit;
 
}
 
$fileName = $_GET['file'];
$fh = @fopen($fileName,'rb');
if (!$fh){
    echo 'Unable to open file';
    exit;
}
$fileSize = filesize($fileName);
header("Content-Type: ".getMimeType($fileName));
header("Content-Length: " . $fileSize);
while(!feof($fh))
{
    //Read the allowed bandwidth, and then just wait a second
    print(fread($fh, $bandwidth*1024));
    usleep(1000000);
}
fclose($fh);
?>

Now, you can create an .htaccess file, to redirect all the requests you need to be limited, to that bandwidth script:

RewriteEngine on
RewriteCond %{REQUEST_URI} \.(gif|jpg|jpeg|png|GIF|JPG|JPEG|PNG)$
RewriteRule (.*) bandwidth.php?file=$1 [L]

If you want you can limit the bandwidth only for some ip’s or some referals. In the htaccess file you will have:

RewriteEngine on

Enter the ip's or class of ip's you want to limit bellow, with [OR] between them

All the other ip's will access the url directly, without going through bandwidth.php

RewriteCond %{REMOTE_ADDR} ^123\.45\.6\.78$ [NC,OR]
RewriteCond %{REMOTE_ADDR} ^127\.0\.0 [NC]
RewriteCond %{REQUEST_URI} \.(gif|jpg|jpeg|png|GIF|JPG|JPEG|PNG)$
RewriteRule (.*) bandwidth.php?file=$1 [L]

Enter the ip's or class of ip's you want to limit bellow, with [OR] between them

All the other ip's will access the url directly, without going through bandwidth.php

RewriteCond %{HTTP_REFERER} ^http://(www\.)?php-code.net/.*$ [NC,OR]
RewriteCond %{HTTP_REFERER} ^http://(www\.)?example.com/.*$ [NC]
RewriteCond %{REQUEST_URI} \.(gif|jpg|jpeg|png|GIF|JPG|JPEG|PNG)$
RewriteRule (.*) bandwidth.php?file=$1 [L]

In this way you can limit the traffic from leecher sites, without forbidden them, which I think is a more elegant solution.

Community
  • 1
  • 1
Shail
  • 1,565
  • 11
  • 24
  • Thanks for that but I don't need to limit the download speed, I need to limit the **upload** speed, per user, if possible :) – Virgiliu Sep 06 '12 at 12:21
  • THis was the idea. Use the same for upload. Split the file in multiple Smaller one. Then upload each chunk and put sleep/usleep there – Shail Sep 06 '12 at 12:29
  • 2
    Most likely because you just say use this same idea for upload. I don't know that you can easily use the same idea for an upload, how can you split a file into pieces that you don't have. Downloads are easy because you control the file, uploads are different because the user controls the file. – Pitchinnate Sep 06 '12 at 13:02
  • @shail: I don't see how I can apply your solution. I can't just "split the file into multiple smaller ones" before getting the whole file. The idea is not to get the whole file as fast as possible. Once the user sends the POST request with the file I can't just "split" the file. – Virgiliu Sep 06 '12 at 14:15
  • Yes, i have implemented with via Perl.I have used that in my php script. Here is the link, Use this to upload files in chunk https://labs.consol.de/blog/misc/fileupload-with-perl-decorated-with-a-progressbar/ – Shail Sep 07 '12 at 05:05
  • That is not useful to me. Maybe my question wasn't explicit enough. The application is a web application, it runs on a server. The users access it via a browser. I cannot read the file from their computer before they upload it completely, can I? Your script hasn't been helpful. – Virgiliu Sep 07 '12 at 08:03