{"id":6767,"date":"2017-09-21T14:39:43","date_gmt":"2017-09-21T12:39:43","guid":{"rendered":"https:\/\/anexia.com\/stagingblog\/?p=6767"},"modified":"2022-04-21T14:13:34","modified_gmt":"2022-04-21T12:13:34","slug":"limit-download-speed-with-codeigniter-and-curl","status":"publish","type":"post","link":"https:\/\/anexia.com\/blog\/en\/limit-download-speed-with-codeigniter-and-curl\/","title":{"rendered":"Limit Download-Speed with CodeIgniter and cURL"},"content":{"rendered":"<p>Many websites, especially download portals, are known for limiting the speed with which files can be downloaded (sometimes to a very significant extent). The business model is generally based on the concept that you can only get the full download speed if you pay for it.<br \/>\nIn this tutorial, I want to show you what\u2019s behind this limitation, and the software tricks these providers are using. I\u2019m also going to show you how to integrate an option to limit download speed into a CodeIgniter project here.<\/p>\n<p><a href=\"https:\/\/anexia.com\/blog\/wp-content\/uploads\/2017\/09\/ci-downloadspeed.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-2940\" src=\"https:\/\/anexia.com\/blog\/wp-content\/uploads\/2017\/09\/ci-downloadspeed.png\" alt=\"Download-Speed-Drosselung mit CodeIgniter\" width=\"599\" height=\"303\" srcset=\"https:\/\/anexia.com\/blog\/wp-content\/uploads\/2017\/09\/ci-downloadspeed.png 1920w, https:\/\/anexia.com\/blog\/wp-content\/uploads\/2017\/09\/ci-downloadspeed-325x164.png 325w, https:\/\/anexia.com\/blog\/wp-content\/uploads\/2017\/09\/ci-downloadspeed-300x152.png 300w, https:\/\/anexia.com\/blog\/wp-content\/uploads\/2017\/09\/ci-downloadspeed-768x388.png 768w, https:\/\/anexia.com\/blog\/wp-content\/uploads\/2017\/09\/ci-downloadspeed-1024x518.png 1024w\" sizes=\"(max-width: 599px) 100vw, 599px\" \/><\/a><\/p>\n<p>&nbsp;<\/p>\n<h2>CURL AS THE CORE OF THE APPLICATION<\/h2>\n<p>When we talk about <a href=\"https:\/\/curl.haxx.se\/\">cURL, <\/a>we are talking about a widely used command line application that is specifically designed to transfer data within computer networks. The advantage is in how easy it is to use, because the program library has been ported to essentially all operating systems. In addition, it also has a vast range of functions.<\/p>\n<p>Unlike its older predecessor \u201cwget\u201d, cURL controls not only the downloading, but also the uploading of files. Numerous parameters provide a correspondingly large number of options, thereby covering nearly every scenario.<\/p>\n<p>&nbsp;<\/p>\n<h2>SETUP WITH CODEIGNITER<\/h2>\n<p>Now we\u2019ve arrived at the essential part of this tutorial: the integration of speed limitation in a CodeIgniter project.<\/p>\n<p><a href=\"https:\/\/anexia.com\/blog\/wp-content\/uploads\/2015\/01\/codeigniter_logo.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-504\" src=\"https:\/\/anexia.com\/blog\/wp-content\/uploads\/2015\/01\/codeigniter_logo.png\" alt=\"CodeIgniter Logo\" width=\"599\" height=\"140\" srcset=\"https:\/\/anexia.com\/blog\/wp-content\/uploads\/2015\/01\/codeigniter_logo.png 2324w, https:\/\/anexia.com\/blog\/wp-content\/uploads\/2015\/01\/codeigniter_logo-600x140.png 600w, https:\/\/anexia.com\/blog\/wp-content\/uploads\/2015\/01\/codeigniter_logo-300x70.png 300w, https:\/\/anexia.com\/blog\/wp-content\/uploads\/2015\/01\/codeigniter_logo-1024x239.png 1024w\" sizes=\"(max-width: 599px) 100vw, 599px\" \/><\/a><\/p>\n<p>&nbsp;<\/p>\n<h2>STEP 1: BUILD THE BASIC SYSTEM<\/h2>\n<p>In order to start our project, we first have to establish a basis on which we can subsequently build. In order to do so, we need to download the latest version of the framework from the <a href=\"https:\/\/www.codeigniter.com\/\">CodeIgniter homepage<\/a>. In this case, this would be version 3.1.5.<\/p>\n<p>Next copy all the files \u2013 ideally to the subfolder \u201cci-downloadspeed\u201d \u2013 onto the webserver (e.g. XAMPP, LAMP, etc.), and open its root directory in your browser, e.g., http:\/\/localhost\/ci-downloadspeed\/. Once all of the requirements for the framework are met, you should then see the CodeIgniter welcome page.<\/p>\n<p>In order to ensure that the speaking URLs can also be accessed correctly, create an <strong>.htaccess<\/strong> file with the following content:<\/p>\n<pre class=\"lang:default decode:true\">RewriteEngine on\r\n\r\nRewriteRule ^(assets|downloads)($|\/) - [L]\r\nRewriteRule .* index.php<\/pre>\n<p>All of the resources needed for the actual application (CSS and JS files) are created in the \u201c<em>assets<\/em>\u201d directory, and the sample download files will later be created in the \u201c<em>downloads<\/em>\u201d directory (information about this can be found in the \u201cConclusion\u201d section).<\/p>\n<p>Next, open <strong>\/application\/config\/autoload.php<\/strong> (the autoloader configuration file) and then add the URL helper. This way, we will be able to use the <em>base_url()<\/em> function both in the frontend and this time, even within the application itself.<\/p>\n<pre class=\"lang:php decode:true\">$autoload['helper'] = array('url');<\/pre>\n<p>In <strong>\/application\/config\/routes.php<\/strong> (the route configuration file), we then need to change the default controller from \u201c<em>welcome<\/em>\u201d to \u201c<em>home<\/em>\u201d, since we want our speed throttling application to be loaded by default later.<\/p>\n<pre class=\"lang:php decode:true\">$route['default_controller'] = 'home';\r\n$route['404_override'] = '';\r\n$route['translate_uri_dashes'] = FALSE;<\/pre>\n<p>&nbsp;<\/p>\n<h2>STEP 2: DEFINE THE BASIC LAYOUT<\/h2>\n<p>In order to do so, navigate to <strong>\/application\/controller <\/strong>and create a file named<strong> Home.php<\/strong> (case-sensitive!).<br \/>\nNext, we need to create the basic framework for our application in this file, which consists of two actions: one for the index page and one for the throttled file downloads. The latter will receive parameters for the type of download (in our example, the parameter will only distinguish between a small file and a large file), as well as parameters for the download speed itself (in KB\/s).<\/p>\n<pre class=\"lang:php decode:true \">&lt;?php\r\ndefined('BASEPATH') OR exit('No direct script access allowed');\r\n\r\nclass Home extends CI_Controller\r\n{\r\n\r\n\r\n\tpublic function index()\r\n\t{\r\n\t\t\r\n\t}\r\n\r\n\r\n\tpublic function download($type = 1, $speed_kbs = 64)\r\n\t{\r\n\t\t\r\n\t}\r\n\r\n}\r\n<\/pre>\n<p>In this case, the index action is very rudimentary, since it has no functionality on its own. It is only being used to display the main page of our application:<\/p>\n<pre class=\"lang:php decode:true \">public function index()\r\n{\r\n\t\/\/ load view\r\n\t$this-&gt;load-&gt;view('home');\r\n}<\/pre>\n<p>Now let\u2019s look at the HTML layout in the linked view.<\/p>\n<pre class=\"lang:php decode:true\">&lt;?php defined('BASEPATH') OR exit('No direct script access allowed'); ?&gt;\r\n&lt;!DOCTYPE html&gt;\r\n&lt;html lang=\"en\" xmlns=\"http:\/\/www.w3.org\/1999\/html\"&gt;\r\n&lt;head&gt;\r\n    &lt;meta charset=\"utf-8\"&gt;\r\n    &lt;meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\"&gt;\r\n    &lt;meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"&gt;\r\n    &lt;meta name=\"description\" content=\"\"&gt;\r\n    &lt;meta name=\"author\" content=\"\"&gt;\r\n\r\n    &lt;title&gt;CI Download-Speed&lt;\/title&gt;\r\n\r\n    &lt;!-- Latest compiled and minified CSS --&gt;\r\n    &lt;link rel=\"stylesheet\" href=\"https:\/\/maxcdn.bootstrapcdn.com\/bootstrap\/3.3.7\/css\/bootstrap.min.css\"\r\n          integrity=\"sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz\/K68vbdEjh4u\" crossorigin=\"anonymous\"&gt;\r\n\r\n    &lt;!-- Custom CSS for basic styling --&gt;\r\n    &lt;link rel=\"stylesheet\" href=\"&lt;?php echo base_url(); ?&gt;assets\/css\/style.css\"\/&gt;\r\n\r\n    &lt;!-- Optional CSS for individual theming (powered by Bootswatch - https:\/\/bootswatch.com\/) --&gt;\r\n    &lt;link rel=\"stylesheet\" href=\"&lt;?php echo base_url(); ?&gt;assets\/css\/bootstrap_flatly.min.css\"\/&gt;\r\n\r\n    &lt;!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries --&gt;\r\n    &lt;!--[if lt IE 9]&gt;\r\n    &lt;script src=\"https:\/\/oss.maxcdn.com\/html5shiv\/3.7.3\/html5shiv.min.js\"&gt;&lt;\/script&gt;\r\n    &lt;script src=\"https:\/\/oss.maxcdn.com\/respond\/1.4.2\/respond.min.js\"&gt;&lt;\/script&gt;\r\n    &lt;![endif]--&gt;\r\n&lt;\/head&gt;\r\n&lt;body&gt;\r\n\r\n&lt;nav class=\"navbar navbar-default navbar-fixed-top\"&gt;\r\n    &lt;div class=\"container\"&gt;\r\n        &lt;div class=\"navbar-header\"&gt;\r\n            &lt;button type=\"button\" class=\"navbar-toggle collapsed\" data-toggle=\"collapse\" data-target=\"#navbar\"\r\n                    aria-expanded=\"false\" aria-controls=\"navbar\"&gt;\r\n                &lt;span class=\"sr-only\"&gt;Toggle navigation&lt;\/span&gt;\r\n                &lt;span class=\"icon-bar\"&gt;&lt;\/span&gt;\r\n                &lt;span class=\"icon-bar\"&gt;&lt;\/span&gt;\r\n                &lt;span class=\"icon-bar\"&gt;&lt;\/span&gt;\r\n            &lt;\/button&gt;\r\n            &lt;a class=\"navbar-brand\" href=\"#\"&gt;CodeIgniter Download-Speed&lt;\/a&gt;\r\n        &lt;\/div&gt;\r\n        &lt;div id=\"navbar\" class=\"collapse navbar-collapse\"&gt;\r\n            &lt;ul class=\"nav navbar-nav\"&gt;\r\n                &lt;li class=\"active\"&gt;&lt;a href=\"&lt;?php echo base_url(); ?&gt;\"&gt;Home&lt;\/a&gt;&lt;\/li&gt;\r\n                &lt;li&gt;&lt;a href=\"#about\"&gt;About&lt;\/a&gt;&lt;\/li&gt;\r\n                &lt;li&gt;&lt;a href=\"#contact\"&gt;Contact&lt;\/a&gt;&lt;\/li&gt;\r\n            &lt;\/ul&gt;\r\n        &lt;\/div&gt;\r\n    &lt;\/div&gt;\r\n&lt;\/nav&gt;\r\n\r\n&lt;div class=\"container\"&gt;\r\n\r\n    &lt;div class=\"intro\"&gt;\r\n        &lt;h1&gt;Choose your download speed!&lt;\/h1&gt;\r\n        &lt;p class=\"lead\"&gt;\r\n            Lorem ipsum dolor sit amet, consetetur sadipscing elitr, &lt;br\/&gt;\r\n            sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, &lt;br\/&gt;\r\n            sed diam voluptua.\r\n        &lt;\/p&gt;\r\n\r\n        Here will be our core application...\r\n        \r\n\r\n\r\n    &lt;\/div&gt;\r\n    \r\n&lt;\/div&gt;\r\n\r\n\r\n&lt;!-- Bootstrap core JavaScript\r\n================================================== --&gt;\r\n&lt;!-- Placed at the end of the document so the pages load faster --&gt;\r\n&lt;script type=\"text\/javascript\" src=\"https:\/\/code.jquery.com\/jquery-3.2.1.min.js\"\r\n        integrity=\"sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=\" crossorigin=\"anonymous\"&gt;&lt;\/script&gt;\r\n&lt;script type=\"text\/javascript\" src=\"https:\/\/maxcdn.bootstrapcdn.com\/bootstrap\/3.3.7\/js\/bootstrap.min.js\"\r\n        integrity=\"sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa\"\r\n        crossorigin=\"anonymous\"&gt;&lt;\/script&gt;\r\n&lt;script type=\"text\/javascript\" src=\"&lt;?php echo base_url(); ?&gt;assets\/js\/app.js\"&gt;&lt;\/script&gt;\r\n&lt;\/body&gt;\r\n&lt;\/html&gt;\r\n<\/pre>\n<p>What we have created here is a basic layout with a navigation bar and a simple block of text with a heading.<\/p>\n<p>Directly underneath it (where it currently still reads \u201c<em>Here will be\u2026<\/em>\u201d) is the markup for our application: a speed meter, which we will configure based on the \u201c<a href=\"http:\/\/www.knowstack.com\/html5-canvas-speedometer\/\" target=\"_blank\" rel=\"noopener\">HTML5 Canvas Speedometer<\/a>\u201d. In addition, there are two download buttons (one for a smaller file, and one for a larger file):<\/p>\n<pre class=\"lang:php decode:true\">&lt;canvas class=\"canvas\" id=\"myCanvas\" width=\"600\" height=\"400\"&gt;\r\n    Your browser does not support the HTML5 canvas tag.\r\n&lt;\/canvas&gt;\r\n\r\n&lt;div class=\"row\"&gt;\r\n    &lt;div class=\"col-md-6 col-md-offset-3\"&gt;\r\n        &lt;div id=\"slider\"&gt;\r\n            &lt;p class=\"lead\"&gt;max. &lt;span id=\"selectedSpeed\"&gt;&lt;\/span&gt; kB\/s&lt;\/p&gt;\r\n\r\n            &lt;input style=\"width:100%\" id=\"slide\" type=\"range\" min=\"0\" max=\"1750\" step=\"50\" value=\"100\"\/&gt;\r\n\r\n        &lt;\/div&gt;\r\n    &lt;\/div&gt;\r\n&lt;\/div&gt;\r\n\r\n\r\n&lt;p&gt;&amp;nbsp;&lt;\/p&gt;\r\n\r\n&lt;p&gt;\r\n    &lt;a href=\"#\" class=\"btn btn-primary btn-lg start-download\"\r\n       data-url=\"&lt;?php echo base_url(); ?&gt;home\/download\/1\/###speed###\"&gt;\r\n        &lt;span class=\"glyphicon glyphicon-download-alt\" aria-hidden=\"true\"&gt;&lt;\/span&gt; Download (100 MB)\r\n    &lt;\/a&gt;\r\n\r\n    &amp;nbsp;\r\n\r\n    &lt;a href=\"#\" class=\"btn btn-primary btn-lg start-download\"\r\n       data-url=\"&lt;?php echo base_url(); ?&gt;home\/download\/2\/###speed###\"&gt;\r\n        &lt;span class=\"glyphicon glyphicon-download-alt\" aria-hidden=\"true\"&gt;&lt;\/span&gt; Download (1 GB)\r\n    &lt;\/a&gt;\r\n&lt;\/p&gt;<\/pre>\n<p>The linked <strong>bootstrap_flatly.min.css<\/strong> file is the free \u201c<a href=\"https:\/\/bootswatch.com\/flatly\/\" target=\"_blank\" rel=\"noopener\">Flatly<\/a>\u201d Bootstrap theme from <a href=\"https:\/\/bootswatch.com\/\" target=\"_blank\" rel=\"noopener\">Bootswatch<\/a>.<br \/>\nThis CSS theme is optional and can therefore be omitted if you prefer.<\/p>\n<p>We will define a couple of basic styles in the<strong> style.css<\/strong> file:<\/p>\n<pre class=\"lang:css decode:true\">body {\r\n\tpadding-top: 50px;\r\n}\r\n.intro {\r\n\tpadding: 40px 15px;\r\n\ttext-align: center;\r\n}\r\n\r\n.intro h1 {\r\n\tmargin-bottom: 40px;\r\n}<\/pre>\n<p>The logic for the frontend of our application belongs in the linked <strong>app.js<\/strong> file.<\/p>\n<pre class=\"lang:js decode:true \">\/\/ Based on:  http:\/\/www.knowstack.com\/html5-canvas-speedometer\/\r\n\r\n$(document).ready(function(){\r\n\r\n    var draw = function(speed)\r\n    {\r\n        var  canvas = document.getElementById(\"myCanvas\");\r\n        var  context = canvas.getContext(\"2d\");\r\n        context.clearRect(0,0,canvas.width, canvas.height);\r\n        var centerX = canvas.width \/ 2;\r\n        var centerY = (canvas.height \/ 5) * 4;\r\n        var radius = canvas.width \/ 2 - 20;\r\n\r\n        context.beginPath();\r\n        context.arc(centerX, centerY, radius, Math.PI*0.10, Math.PI*-1.1, true);\r\n\r\n        var gradience = context.createRadialGradient(centerX, centerY, radius-radius\/2, centerX, centerY, radius-radius\/8);\r\n        gradience.addColorStop(0, '#ffffff');\r\n        gradience.addColorStop(1, '#18bc9c');\r\n\r\n        context.fillStyle = gradience;\r\n        context.fill();\r\n        context.closePath();\r\n        context.restore();\r\n\r\n        context.beginPath();\r\n        context.strokeStyle = '#ffffff';\r\n        context.translate(centerX,centerY);\r\n        var increment = 50;\r\n        context.font=\"15px Helvetica\";\r\n        for (var i=-18; i&lt;=18; i++)\r\n        {\r\n            angle = Math.PI\/30*i;\r\n            sineAngle = Math.sin(angle);\r\n            cosAngle = -Math.cos(angle);\r\n\r\n            if (i % 5 == 0) {\r\n                context.lineWidth = 8;\r\n                iPointX = sineAngle *(radius -radius\/4);\r\n                iPointY = cosAngle *(radius -radius\/4);\r\n                oPointX = sineAngle *(radius -radius\/7);\r\n                oPointY = cosAngle *(radius -radius\/7);\r\n\r\n                wPointX = sineAngle *(radius -radius\/2.5);\r\n                wPointY = cosAngle *(radius -radius\/2.5);\r\n                context.fillText((i+18)*increment,wPointX-2,wPointY+4);\r\n            }\r\n            else\r\n            {\r\n                context.lineWidth = 2;\r\n                iPointX = sineAngle *(radius -radius\/5.5);\r\n                iPointY = cosAngle *(radius -radius\/5.5);\r\n                oPointX = sineAngle *(radius -radius\/7);\r\n                oPointY = cosAngle *(radius -radius\/7);\r\n            }\r\n            context.beginPath();\r\n            context.moveTo(iPointX,iPointY);\r\n            context.lineTo(oPointX,oPointY);\r\n            context.stroke();\r\n            context.closePath();\r\n\r\n        }\r\n\r\n        var numOfSegments = speed\/increment;\r\n        numOfSegments = numOfSegments -18;\r\n        angle = Math.PI\/30*numOfSegments;\r\n        sineAngle = Math.sin(angle);\r\n        cosAngle = -Math.cos(angle);\r\n        pointX = sineAngle *(3\/4*radius);\r\n        pointY = cosAngle *(3\/4*radius);\r\n\r\n        context.beginPath();\r\n        context.strokeStyle = '#2c3e50';\r\n        context.arc(0, 0, 19, 0, 2*Math.PI, true);\r\n        context.fill();\r\n        context.closePath();\r\n\r\n        context.beginPath();\r\n        context.lineWidth=6;\r\n\r\n        context.moveTo(0,0);\r\n        context.lineTo(pointX,pointY);\r\n\r\n        context.stroke();\r\n        context.closePath();\r\n        context.restore();\r\n        context.translate(-centerX,-centerY);\r\n    };\r\n\r\n    \r\n    \/\/ TODO: add listener for range-field and function for setting the selected speed\r\n\r\n});<\/pre>\n<p>This takes care of the graphical canvas rendering of the speed meter. We now expand this file to include our actual functions (important: it is essential that you write where \u201c<em>TODO: add listener\u2026<\/em>\u201d is currently located within the \u201cdocument.ready()\u201d block!). There you will find, among other things, the central function used to set the selected download speed (\u201c<em>setSpeed()<\/em>\u201c) and a listener, which will react to the range input field. In due course, the listener will transfer the selected value into the speed meter, and will dynamically adjust our two download links.<\/p>\n<pre class=\"lang:js decode:true\">var setSpeed = function(speed)\r\n{\r\n    \/\/ prevent a speed of zero\r\n    if( speed == 0 ){\r\n        speed = 1;\r\n    }\r\n\r\n    \/\/ draw speedmeter graphic\r\n    draw(speed);\r\n\r\n    \/\/ write text under speedmeter graphic\r\n    $('#selectedSpeed').text(speed);\r\n\r\n    \/\/ set individual download url with dynamically speed assignment\r\n    $('a.start-download').each(function(i, v){\r\n        var url = $(v).data('url');\r\n        url = url.replace('###speed###', speed);\r\n\r\n        $(v).attr('href', url);\r\n    });\r\n};\r\n\r\n\r\n\/\/ set inital speed\r\nsetSpeed(100);\r\n\r\n\r\n\/\/ check for change of speed value while dragging\r\n$(document).on('input change', '#slide', function() {\r\n    setSpeed( $(this).val() );\r\n});<\/pre>\n<p>Because the listener will dynamically adjust both download links, we didn\u2019t set the link target for the two download buttons directly, but instead just set it via a <a href=\"http:\/\/api.jquery.com\/data\/\" target=\"_blank\" rel=\"noopener\">data<\/a> attribute using a placeholder for the actual speed. In this way, we can use the attribute as a template, and we can keep setting the link target dynamically. Initially, we set the download speed to 100 KB\/s. This is the default setting that a user gets when they open our application.<\/p>\n<p>&nbsp;<\/p>\n<h2>STEP 3: INTEGRATING LIMITATION<\/h2>\n<p>To integrate speed limitation, we now need our download action, the structure of which we have already created.<\/p>\n<pre class=\"lang:php decode:true \">public function download($type = 1, $speed_kbs = 64)\r\n{\r\n\t\/\/ detect, which example file should be used\r\n\tif ($type == 2) {\r\n\t\t$filename = '1000mb.bin';\r\n\t} else {\r\n\t\t$filename = '100mb.bin';\r\n\t}\r\n\r\n\t\/\/ build up public URL for file (for public access via cURL)\r\n\t$public_filename = base_url() . 'downloads\/' . $filename;\r\n\r\n\t\/\/ build up internal URL for file (for internal access, like filesize())\r\n\t$local_filename = FCPATH . 'downloads\/' . $filename;\r\n\r\n\r\n\t\/\/ get speed in Bytes\/s\r\n\t$speed = $speed_kbs * 1000;\r\n\r\n\t\/\/ prepare headers for forcing file download\r\n\theader('Content-Type: application\/octet-stream');\r\n\theader('Content-Length: ' . filesize($local_filename));\r\n\theader('Content-Disposition: attachment; filename=\"' . basename($filename) . '\"');\r\n\r\n\r\n\t\/\/ TODO: get file and provide it as streamed download (with speed limitation)\r\n}<\/pre>\n<p>In our example, we first check in this download action whether the large or the small sample file is being loaded. Once this check has been performed, we build the public URL (with domain or IP, depending on the configuration) and the internal URL (absolute path on the server). At the same time, we calculate our download speed by converting the KB\/s into bytes\/s (using a factor of 1000).<\/p>\n<p>We subsequently define the PHP header for a file download. In order to do so, we simply use \u201c<em>application\/octet-stream<\/em>\u201d as a \u201ccontent type\u201d so that the download is always forced and the browser doesn\u2019t try to open or display the file itself. Moreover, information regarding the size of the file now (optionally) follows so that the browser is able to detect and display the progress of the download. The name of the file itself comes at the end.<\/p>\n<p>The actual download then follows. We use cURL for this, and we set its parameters as follows:<\/p>\n<pre class=\"lang:php decode:true \">\/\/ init cURL\r\n$ch = curl_init();\r\n\r\n\/\/ read file from public URL\r\ncurl_setopt($ch, CURLOPT_URL, $public_filename);\r\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\r\ncurl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 500);\r\n\r\n\/\/ limit download speed (in bytes per second)\r\ncurl_setopt($ch, CURLOPT_MAX_RECV_SPEED_LARGE, $speed);\r\n\r\n\/\/ stream data in realtime, instead of locally download before finally delivery\r\ncurl_setopt($ch, CURLOPT_WRITEFUNCTION, function ($curl, $data) {\r\n\techo $data;\r\n\treturn strlen($data);\r\n});\r\n\r\n\/\/ perform \"cURL-query\"\r\ncurl_exec($ch);\r\n\r\n\/\/ close cURL session\r\ncurl_close($ch);<\/pre>\n<p>These are essentially mostly standard parameters with two exceptions: <a href=\"https:\/\/curl.haxx.se\/libcurl\/c\/CURLOPT_MAX_RECV_SPEED_LARGE.html\" target=\"_blank\" rel=\"noopener\"><strong>CURLOPT_MAX_RECV_SPEED_LARGE<\/strong><\/a> and <a href=\"https:\/\/curl.haxx.se\/libcurl\/c\/CURLOPT_WRITEFUNCTION.html\" target=\"_blank\" rel=\"noopener\"><strong>CURLOPT_WRITEFUNCTION<\/strong><\/a>.<\/p>\n<p>The <strong>CURLOPT_MAX_RECV_SPEED_LARGE<\/strong> option allows us to determine the maximum download speed with which the cURL library reads the data. The task here is in bytes\/s, which is the reason for our previous conversion. If the value \u201c0\u201d is specified, there is no throttling and the download is performed with no limits on the speed. The actual speed thus depends more on external factors such as the internet connection itself.<\/p>\n<p>We indicate our own output handler using <strong>CURLOPT_WRITEFUNCTION<\/strong> so that the data being read by cURL can also be streamed directly to the user, and not loaded locally onto the server for minutes (or even hours, depending on the speed!) first, before it is delivered. This ensures that the content received is forwarded to the user 1:1 in real time.<\/p>\n<p>To summarize, our download action now appears as follows:<\/p>\n<pre class=\"lang:php decode:true \">public function download($type = 1, $speed_kbs = 64)\r\n{\r\n\t\/\/ detect, which example file should be used\r\n\tif ($type == 2) {\r\n\t\t$filename = '1000mb.bin';\r\n\t} else {\r\n\t\t$filename = '100mb.bin';\r\n\t}\r\n\r\n\t\/\/ build up public URL for file (for public access via cURL)\r\n\t$public_filename = base_url() . 'downloads\/' . $filename;\r\n\r\n\t\/\/ build up internal URL for file (for internal access, like filesize())\r\n\t$local_filename = FCPATH . 'downloads\/' . $filename;\r\n\r\n\r\n\t\/\/ get speed in KB\/s\r\n\t$speed = $speed_kbs * 1000;\r\n\r\n\t\/\/ prepare headers for forcing file download\r\n\theader('Content-Type: application\/octet-stream');\r\n\theader('Content-Length: ' . filesize($local_filename));\r\n\theader('Content-Disposition: attachment; filename=\"' . basename($filename) . '\"');\r\n\r\n\r\n\t\/\/ init cURL\r\n\t$ch = curl_init();\r\n\r\n\t\/\/ read file from public URL\r\n\tcurl_setopt($ch, CURLOPT_URL, $public_filename);\r\n\tcurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\r\n\tcurl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 500);\r\n\r\n\t\/\/ limit download speed (in bytes per second)\r\n\tcurl_setopt($ch, CURLOPT_MAX_RECV_SPEED_LARGE, $speed);\r\n\r\n\t\/\/ stream data in realtime, instead of locally download before finally delivery\r\n\tcurl_setopt($ch, CURLOPT_WRITEFUNCTION, function ($curl, $data) {\r\n\t\techo $data;\r\n\t\treturn strlen($data);\r\n\t});\r\n\r\n\t\/\/ perform \"cURL-query\"\r\n\tcurl_exec($ch);\r\n\r\n\t\/\/ close cURL session\r\n\tcurl_close($ch);\r\n}<\/pre>\n<p>&nbsp;<\/p>\n<h2>CONCLUSION<\/h2>\n<p>Speed limitation for file downloads is quick and easy to implement using the cURL library. All that is necessary is to configure cURL correctly.<\/p>\n<p>In our sample project, it is currently also possible to access downloads directly using their public URL. This is necessary for the internal download via cURL, since cURL is essentially functioning as a proxy here. In practice, however, the possibility of direct access should be prevented. Assuming that the server has a static IP address, it is possible to place an .htaccess in the \u201c<em>downloads<\/em>\u201d folder, for example, which would only allow the server to be accessed using the server IP address (alternatively, vHost-Config could also be used).<\/p>\n<p>Here is an example of this kind of .htaccess file (in this case, 188.65.77.77 would be the IP of the server on which the application is running):<\/p>\n<pre class=\"lang:default decode:true \">Deny from all\r\nAllow from 188.65.77.77<\/pre>\n<p>This would ensure that the application could access the public URL for the purposes of internal downloads, but that no other users would have access. It would also be possible to load the files from a remote server, and that the meta-information (such as file size) could be obtained from a database.<\/p>\n<p>The two linked dummy download files could be obtained via the <a href=\"https:\/\/anexia.com\/map\/\" target=\"_blank\" rel=\"noopener\">Anexia Network Map <\/a>or alternatively, directly via these links, among other options: <a href=\"http:\/\/37.252.235.199\/100mb.bin\" target=\"_blank\" rel=\"noopener\">100MB<\/a> and <a href=\"http:\/\/37.252.235.199\/1000mb.bin\" target=\"_blank\" rel=\"noopener\">1GB<\/a> (via the <a href=\"http:\/\/www.datasix.at\/\" target=\"_blank\" rel=\"noopener\">DATASIX<\/a> datacenter).<\/p>\n<p>We have been working with the CodeIgniter framework in this tutorial because it is very easy to understand and requires almost no training time. In principle, however, any framework could be used, for example <a href=\"https:\/\/laravel.com\/\" target=\"_blank\" rel=\"noopener\">Laravel <\/a>or <a href=\"https:\/\/framework.zend.com\/\" target=\"_blank\" rel=\"noopener\">Zend<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In this tutorial, I want to show you how to integrate an option to limit download speed into a CodeIgniter project.<\/p>\n","protected":false},"author":15,"featured_media":2961,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1135],"tags":[1657,1681,1683,1685,1329,1630,1570],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v22.2 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Limit Download-Speed with CodeIgniter and cURL - ANEXIA Blog<\/title>\n<meta name=\"description\" content=\"In this tutorial, I want to show you how to integrate an option to limit download speed into a CodeIgniter project.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/anexia.com\/blog\/en\/limit-download-speed-with-codeigniter-and-curl\/\" \/>\n<meta property=\"og:locale\" content=\"de_DE\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Limit Download-Speed with CodeIgniter and cURL - ANEXIA Blog\" \/>\n<meta property=\"og:description\" content=\"In this tutorial, I want to show you how to integrate an option to limit download speed into a CodeIgniter project.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/anexia.com\/blog\/en\/limit-download-speed-with-codeigniter-and-curl\/\" \/>\n<meta property=\"og:site_name\" content=\"ANEXIA Blog\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/anexiagmbh\/\" \/>\n<meta property=\"article:published_time\" content=\"2017-09-21T12:39:43+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2022-04-21T12:13:34+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/anexia.com\/blog\/wp-content\/uploads\/2017\/09\/ManuelWutte-Blog-Teaser.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"672\" \/>\n\t<meta property=\"og:image:height\" content=\"372\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Manuel Wutte\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@_ANEXIA\" \/>\n<meta name=\"twitter:site\" content=\"@_ANEXIA\" \/>\n<meta name=\"twitter:label1\" content=\"Verfasst von\" \/>\n\t<meta name=\"twitter:data1\" content=\"Manuel Wutte\" \/>\n\t<meta name=\"twitter:label2\" content=\"Gesch\u00e4tzte Lesezeit\" \/>\n\t<meta name=\"twitter:data2\" content=\"15\u00a0Minuten\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/anexia.com\/blog\/en\/limit-download-speed-with-codeigniter-and-curl\/\",\"url\":\"https:\/\/anexia.com\/blog\/en\/limit-download-speed-with-codeigniter-and-curl\/\",\"name\":\"Limit Download-Speed with CodeIgniter and cURL - ANEXIA Blog\",\"isPartOf\":{\"@id\":\"https:\/\/anexia.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/anexia.com\/blog\/en\/limit-download-speed-with-codeigniter-and-curl\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/anexia.com\/blog\/en\/limit-download-speed-with-codeigniter-and-curl\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/anexia.com\/blog\/wp-content\/uploads\/2017\/09\/ManuelWutte-Blog-Teaser.jpg\",\"datePublished\":\"2017-09-21T12:39:43+00:00\",\"dateModified\":\"2022-04-21T12:13:34+00:00\",\"author\":{\"@id\":\"https:\/\/anexia.com\/blog\/#\/schema\/person\/926f6b9e5aeed88b145cf86d87fd09de\"},\"description\":\"In this tutorial, I want to show you how to integrate an option to limit download speed into a CodeIgniter project.\",\"breadcrumb\":{\"@id\":\"https:\/\/anexia.com\/blog\/en\/limit-download-speed-with-codeigniter-and-curl\/#breadcrumb\"},\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/anexia.com\/blog\/en\/limit-download-speed-with-codeigniter-and-curl\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\/\/anexia.com\/blog\/en\/limit-download-speed-with-codeigniter-and-curl\/#primaryimage\",\"url\":\"https:\/\/anexia.com\/blog\/wp-content\/uploads\/2017\/09\/ManuelWutte-Blog-Teaser.jpg\",\"contentUrl\":\"https:\/\/anexia.com\/blog\/wp-content\/uploads\/2017\/09\/ManuelWutte-Blog-Teaser.jpg\",\"width\":672,\"height\":372},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/anexia.com\/blog\/en\/limit-download-speed-with-codeigniter-and-curl\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/anexia.com\/blog\/de\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Limit Download-Speed with CodeIgniter and cURL\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/anexia.com\/blog\/#website\",\"url\":\"https:\/\/anexia.com\/blog\/\",\"name\":\"ANEXIA Blog\",\"description\":\"[:de] ANEXIA Blog - Technischen Themen, Anexia News und Insights [:]\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/anexia.com\/blog\/?s={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"de\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/anexia.com\/blog\/#\/schema\/person\/926f6b9e5aeed88b145cf86d87fd09de\",\"name\":\"Manuel Wutte\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\/\/anexia.com\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/9528a61f48f4294cd5f7cd8a4141bd55?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/9528a61f48f4294cd5f7cd8a4141bd55?s=96&d=mm&r=g\",\"caption\":\"Manuel Wutte\"},\"url\":\"https:\/\/anexia.com\/blog\/author\/mwu\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Limit Download-Speed with CodeIgniter and cURL - ANEXIA Blog","description":"In this tutorial, I want to show you how to integrate an option to limit download speed into a CodeIgniter project.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/anexia.com\/blog\/en\/limit-download-speed-with-codeigniter-and-curl\/","og_locale":"de_DE","og_type":"article","og_title":"Limit Download-Speed with CodeIgniter and cURL - ANEXIA Blog","og_description":"In this tutorial, I want to show you how to integrate an option to limit download speed into a CodeIgniter project.","og_url":"https:\/\/anexia.com\/blog\/en\/limit-download-speed-with-codeigniter-and-curl\/","og_site_name":"ANEXIA Blog","article_publisher":"https:\/\/www.facebook.com\/anexiagmbh\/","article_published_time":"2017-09-21T12:39:43+00:00","article_modified_time":"2022-04-21T12:13:34+00:00","og_image":[{"width":672,"height":372,"url":"https:\/\/anexia.com\/blog\/wp-content\/uploads\/2017\/09\/ManuelWutte-Blog-Teaser.jpg","type":"image\/jpeg"}],"author":"Manuel Wutte","twitter_card":"summary_large_image","twitter_creator":"@_ANEXIA","twitter_site":"@_ANEXIA","twitter_misc":{"Verfasst von":"Manuel Wutte","Gesch\u00e4tzte Lesezeit":"15\u00a0Minuten"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/anexia.com\/blog\/en\/limit-download-speed-with-codeigniter-and-curl\/","url":"https:\/\/anexia.com\/blog\/en\/limit-download-speed-with-codeigniter-and-curl\/","name":"Limit Download-Speed with CodeIgniter and cURL - ANEXIA Blog","isPartOf":{"@id":"https:\/\/anexia.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/anexia.com\/blog\/en\/limit-download-speed-with-codeigniter-and-curl\/#primaryimage"},"image":{"@id":"https:\/\/anexia.com\/blog\/en\/limit-download-speed-with-codeigniter-and-curl\/#primaryimage"},"thumbnailUrl":"https:\/\/anexia.com\/blog\/wp-content\/uploads\/2017\/09\/ManuelWutte-Blog-Teaser.jpg","datePublished":"2017-09-21T12:39:43+00:00","dateModified":"2022-04-21T12:13:34+00:00","author":{"@id":"https:\/\/anexia.com\/blog\/#\/schema\/person\/926f6b9e5aeed88b145cf86d87fd09de"},"description":"In this tutorial, I want to show you how to integrate an option to limit download speed into a CodeIgniter project.","breadcrumb":{"@id":"https:\/\/anexia.com\/blog\/en\/limit-download-speed-with-codeigniter-and-curl\/#breadcrumb"},"inLanguage":"de","potentialAction":[{"@type":"ReadAction","target":["https:\/\/anexia.com\/blog\/en\/limit-download-speed-with-codeigniter-and-curl\/"]}]},{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/anexia.com\/blog\/en\/limit-download-speed-with-codeigniter-and-curl\/#primaryimage","url":"https:\/\/anexia.com\/blog\/wp-content\/uploads\/2017\/09\/ManuelWutte-Blog-Teaser.jpg","contentUrl":"https:\/\/anexia.com\/blog\/wp-content\/uploads\/2017\/09\/ManuelWutte-Blog-Teaser.jpg","width":672,"height":372},{"@type":"BreadcrumbList","@id":"https:\/\/anexia.com\/blog\/en\/limit-download-speed-with-codeigniter-and-curl\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/anexia.com\/blog\/de\/"},{"@type":"ListItem","position":2,"name":"Limit Download-Speed with CodeIgniter and cURL"}]},{"@type":"WebSite","@id":"https:\/\/anexia.com\/blog\/#website","url":"https:\/\/anexia.com\/blog\/","name":"ANEXIA Blog","description":"[:de] ANEXIA Blog - Technischen Themen, Anexia News und Insights [:]","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/anexia.com\/blog\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"de"},{"@type":"Person","@id":"https:\/\/anexia.com\/blog\/#\/schema\/person\/926f6b9e5aeed88b145cf86d87fd09de","name":"Manuel Wutte","image":{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/anexia.com\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/9528a61f48f4294cd5f7cd8a4141bd55?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/9528a61f48f4294cd5f7cd8a4141bd55?s=96&d=mm&r=g","caption":"Manuel Wutte"},"url":"https:\/\/anexia.com\/blog\/author\/mwu\/"}]}},"lang":"en","translations":{"en":6767,"de":2939},"amp_enabled":true,"pll_sync_post":[],"_links":{"self":[{"href":"https:\/\/anexia.com\/blog\/wp-json\/wp\/v2\/posts\/6767"}],"collection":[{"href":"https:\/\/anexia.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/anexia.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/anexia.com\/blog\/wp-json\/wp\/v2\/users\/15"}],"replies":[{"embeddable":true,"href":"https:\/\/anexia.com\/blog\/wp-json\/wp\/v2\/comments?post=6767"}],"version-history":[{"count":1,"href":"https:\/\/anexia.com\/blog\/wp-json\/wp\/v2\/posts\/6767\/revisions"}],"predecessor-version":[{"id":6770,"href":"https:\/\/anexia.com\/blog\/wp-json\/wp\/v2\/posts\/6767\/revisions\/6770"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/anexia.com\/blog\/wp-json\/wp\/v2\/media\/2961"}],"wp:attachment":[{"href":"https:\/\/anexia.com\/blog\/wp-json\/wp\/v2\/media?parent=6767"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/anexia.com\/blog\/wp-json\/wp\/v2\/categories?post=6767"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/anexia.com\/blog\/wp-json\/wp\/v2\/tags?post=6767"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}