An MIT licensed code snippet which, allows you to force a download of a file, Give that download file a name, Limit the speed of the download in KB/s and allow resumable downloads for users.

<?php
/* 
 * -------------------------------------------------------
 * forceDownload
 * -------------------------------------------------------
 * @Version: 1.0.0
 * @Author:  FireDart
 * @Link:    http://firedartstudios.com/
 * @GitHub:  https://github.com/FireDart/snippets/Downloads
 * @License: The MIT License (MIT)
 * 
 * Allows you to:
 * + Force a download of a file
 * + Give that download file a name
 * + Limit the speed of the download in KB/s
 * + Allow resumable downloads
 * 
 * -------------------------------------------------------
 * WARNING
 * -------------------------------------------------------
 * MAKE SURE YOU SANATIZE DOWNLOAD PATHS IF IT IS A USER 
 * INPUT, OTHERWISE ATTACKS CAN GAIN ACCESS TO YOUR INTERAL 
 * FILE SYSTEM.
 * 
 * -------------------------------------------------------
 * Recommendations
 * -------------------------------------------------------
 * This script does provide the ability to limit the users 
 * download rate, however, it is suggested that you use an 
 * alternative in apache2, nginx, Lighttpd or what other 
 * software you are using to host your site.
 * 
 * apache2 (No built in feature use mod_bw)
 * - http://bwmod.sourceforge.net/
 * - sudo apt-get install libapache2-mod-bw
 * - sudo a2enmod bw
 * - Guide to install: 
 * - http://freedif.org/bandwidth-restriction-how-to-limit-the-download-speed-for-your-visitors/
 * nginx
 * - http://wiki.nginx.org/NginxHttpCoreModule#limit_rate
 * Lighttpd
 * - http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs_TrafficShaping#Selective-traffic-shaping-plugin15-svn
 * 
 * -------------------------------------------------------
 * Requirements
 * -------------------------------------------------------
 * PHP 5.3.0+
 * 
 * -------------------------------------------------------
 * Usage
 * -------------------------------------------------------
 * Basic
 * forceDownload("path/to/file.ext");
 * 
 * All features
 * forceDownload("path/to/file.ext", "myFileName.ext", 1000);
 * 
 */
/* 
 * forceDownload
 * 
 * Forces the page to download the specified file
 * 
 * @param str $download The path to the file you want to force a download
 * @param str $name     The name of the file the user will see when he downloads
 * @param int $speed    The speed in kb/s of the download, (ex. 1000KB/s = 1MB/s)
 * @return mixed
 */
function forceDownload($download, $name = null, $speed = null) {
    try {
        // Try and disable gzip compression for IE
        if(ini_get('zlib.output_compression')) {
            ini_set('zlib.output_compression', 'Off');
        }
        // Can the file been found?
        if(!file_exists($download) && !is_file($download)) {
            throw new Exception('Unable to find specified download, are you point to a valid file?');
        }
        // Get a name for our file
        if($name == null) {
            $name  = basename($download);
        }
        // Get file extension
        // This is split apart to follow PHP strict standard
        $extension = explode('.', $download);
        $extension = end($extension);
        $extension = strtolower($extension);
        // Security check
        $blacklist = array("php", "ini", "conf");
        if(in_array($extension, $blacklist)) {
            throw new Exception('Sorry but this type of file is blacklisted and can\'t be downloaded unless the administrator white lists it.');
        }
        // Check that the provided $name is the same file type
        // This is split apart to follow PHP strict standard
        $nameExten = explode('.', $name);
        $nameExten = end($nameExten);
        $nameExten = strtolower($nameExten);
        if($nameExten != $extension) {
            throw new Exception('The name of the file you supplied does not match the original download extension.');
        }
        // Get file size
        $fileSize  = filesize($download);
        // Content-Types
        $ctype     = "application/octet-stream";
        // List of specific ctypes, add more if can
        $ctypes    = array(
            // General
             "txt" => "text/plain",
             "htm" => "text/html",
            "html" => "text/html",
             "css" => "text/css",
              "js" => "application/javascript",
            "json" => "application/json",
             "xml" => "application/xml",
             
            // Archive
             "zip" => "application/zip",
             "rar" => "application/x-rar-compressed",
              "7z" => "application/x-7z-compressed",
             "exe" => "application/octet-stream",
             "msi" => "application/x-msdownload",
             "cab" => "application/vnd.ms-cab-compressed",
            
            // Images
            "jpeg" => "image/jpg",
             "jpg" => "image/jpg",
             "png" => "image/png",
             "gif" => "image/gif",
            "webp" => "image/webp",
             "bmp" => "image/bmp",
             "ico" => "image/vnd.microsoft.icon",
            "tiff" => "image/tiff",
             "tif" => "image/tiff",
             "svg" => "image/svg+xml",
             "svgz" => "image/svg+xml",
             
            // Audio
             "mp3" => "audio/mpeg",
            "flac" => "audio/x-flac",
             "ogg" => "audio/ogg",
             "wma" => "audio/x-ms-wma",
             
            // Video
             "mp4" => "video/mp4",
             "mkv" => "video/x-matroska, audio/x-matroska",
            "webm" => "video/webm, audio/webm",
             "ogv" => "video/ogv",
             "wmv" => "video/x-ms-wmv",
             "mpg" => "video/mpeg",
             "avi" => "video/x-msvideo",
             
            // Adobe
             "pdf" => "application/pdf",
             "psd" => "image/vnd.adobe.photoshop",
              "ai" => "application/postscript",
             "eps" => "application/postscript",
              "ps" => "application/postscript",

            // MS Office
             "doc" => "application/msword",
             "rtf" => "application/rtf",
             "xls" => "application/vnd.ms-excel",
             "ppt" => "application/vnd.ms-powerpoint",

            // LibreOffice
             "odt" => "application/vnd.oasis.opendocument.text",
             "ods" => "application/vnd.oasis.opendocument.spreadsheet",
        );
        // Use specific Content-Type if we can
        if(isset($ctypes[$extension])) {
            $ctype = $ctypes[$extension];
        }
        // We got everything we need, send headers & start file download
        // We will be sending chunks for more reliable transfer & to allow resumable downloads
        $file = fopen($download, "rb"); // rb, b is for Windows but might as well use it on Linux
        if(isset($speed) && is_int($speed)) {
            // We make the script sleep for a 1 second below for every chunk
            $chunk = 1024 * $speed;
        } else {
            // 8KB so we don't kill the server, this will go as fast as the script executes (not per second)
            $chunk = 1024 * 8;
        }
        // Start processing file
        if(!$file) {
            throw new Exception('Could not open file.');
        } else {
            // Set headers
            header('Content-Description: File Transfer');
            // Size
            if(isset($_SERVER['HTTP_RANGE'])) {
                file_put_contents("logs.txt", $_SERVER['HTTP_RANGE'] . "\n", FILE_APPEND);
                // Learn about range:
                // http://tools.ietf.org/id/draft-ietf-http-range-retrieval-00.txt	  range1, range2/total
                list($param, $range) = explode('=', $_SERVER['HTTP_RANGE']); // bytes=0-1024,123-124/total
                // Bad request - range unit is not 'bytes'
                if(strtolower(trim($param)) != 'bytes') {
                    header("HTTP/1.1 400 Invalid Request");
                    exit;
                }
                // Split ranges
                $range = explode(',', $range);
                // We only deal with the first requested range
                $range = explode('-', $range[0]);
                // Bad request - 'bytes' parameter is not valid
                if(count($range) != 2) {
                    header("HTTP/1.1 400 Invalid Request");
                    exit;
                }
                // Check range for valid values
                if(empty($range[0])) {
                    $offset = 0;
                } else {
                    $offset = $range[0];
                }
                if(empty($range[1])) {
                    $end = $fileSize - 1;
                } else {
                    $end = $range[1];
                }
                if($offset > 0 || $end < ($filesize - 1)) {
                    header("HTTP/1.1 206 Partial Content");
                }
                // Allow resumable downloads
                header("Accept-Ranges: bytes");
                header("Content-Range: bytes $offset-$end/$fileSize");
            } else {
                $offset = 0;
            }
            header("Content-Length: " . $fileSize);
            // IE 6 fix
            header("Pragma: public");
            header("Cache-Control: public, must-revalidate, post-check=0, pre-check=0");
            // Prevent Caching
            header("Expires: -1");
            // Type
            header("Content-Type: " . $ctype);
            // Attach file
            header("Content-Disposition: attachment; filename=\"$name\"");
            // Do we need to start at a specific amount?
            fseek($file, $offset);
            // Begin download
            ob_start();
            while(!feof($file)) {
                echo fread($file, $chunk);
                flush();
                ob_flush();
                // If a speed limit is set, limit every 1 second
                if(isset($speed)) {
                    sleep(1);
                }
            }
            fclose($file);
            exit;
        }
    // Catch all errors and report back
    } catch(Exception $e) {
        echo $e->getMessage();
    }
}