Welcome Guest, Not a member yet? Register   Sign In
force_download improved (partial download, speed limit and more)
#1

[eluser]Wilker[/eluser]
Hi all!

It's my first time in forum, and I come to collaborate with one improved version of force_download (in download helper)

this version have some great features:

- support to pause/resume downloads
- support segmented downloads
- support download speed limit
- improved performance for file downloads (preventing memory leaks with big files)

the signature is compatible with the current version, but you can use some different ways like:

force_download('server_file_path.zip')

this way the improved version is for files is called, and I add more 2 arguments:

force_download($filename = '', $data = false, $enable_partial = true, $speedlimit = 0)

if data is false, the scripts tries to open the filename in server
if enable_partial is true, the scripts accepts http_range protocol
if speed_limit is greater than zero, the script will limit the user download speed

here is the code:

Code:
function force_download($filename = '', $data = false, $enable_partial = true, $speedlimit = 0)
    {
        if ($filename == '')
        {
            return FALSE;
        }
        
        if($data === false && !file_exists($filename))
            return FALSE;

        // Try to determine if the filename includes a file extension.
        // We need it in order to set the MIME type
        if (FALSE === strpos($filename, '.'))
        {
            return FALSE;
        }
    
        // Grab the file extension
        $x = explode('.', $filename);
        $extension = end($x);

        // Load the mime types
        @include(APPPATH.'config/mimes'.EXT);
    
        // Set a default mime if we can't find it
        if ( ! isset($mimes[$extension]))
        {
            if (ereg('Opera(/| )([0-9].[0-9]{1,2})', $_SERVER['HTTP_USER_AGENT']))
                $UserBrowser = "Opera";
            elseif (ereg('MSIE ([0-9].[0-9]{1,2})', $_SERVER['HTTP_USER_AGENT']))
                $UserBrowser = "IE";
            else
                $UserBrowser = '';
            
            $mime = ($UserBrowser == 'IE' || $UserBrowser == 'Opera') ? 'application/octetstream' : 'application/octet-stream';
        }
        else
        {
            $mime = (is_array($mimes[$extension])) ? $mimes[$extension][0] : $mimes[$extension];
        }
        
        $size = $data === false ? filesize($filename) : strlen($data);
        
        if($data === false)
        {
            $info = pathinfo($filename);
            $name = $info['basename'];
        }
        else
        {
            $name = $filename;
        }
        
        // Clean data in cache if exists
        @ob_end_clean();
        
        // Check for partial download
        if(isset($_SERVER['HTTP_RANGE']) && $enable_partial)
        {
            list($a, $range) = explode("=", $_SERVER['HTTP_RANGE']);
            list($fbyte, $lbyte) = explode("-", $range);
            
            if(!$lbyte)
                $lbyte = $size - 1;
            
            $new_length = $lbyte - $fbyte;
            
            header("HTTP/1.1 206 Partial Content", true);
            header("Content-Length: $new_length", true);
            header("Content-Range: bytes $fbyte-$lbyte/$size", true);
        }
        else
        {
            header("Content-Length: " . $size);
        }
        
        // Common headers
        header('Content-Type: ' . $mime, true);
        header('Content-Disposition: attachment; filename="' . $name . '"', true);
        header("Expires: Mon, 26 Jul 1997 05:00:00 GMT", true);
        header('Accept-Ranges: bytes', true);
        header("Cache-control: private", true);
        header('Pragma: private', true);
        
        // Open file
        if($data === false) {
            $file = fopen($filename, 'r');
            
            if(!$file)
                return FALSE;
        }
        
        // Cut data for partial download
        if(isset($_SERVER['HTTP_RANGE']) && $enable_partial)
            if($data === false)
                fseek($file, $range);
            else
                $data = substr($data, $range);
        
        // Disable script time limit
        @set_time_limit(0);
        
        // Check for speed limit or file optimize
        if($speedlimit > 0 || $data === false)
        {
            if($data === false)
            {
                $chunksize = $speedlimit > 0 ? $speedlimit * 1024 : 512 * 1024;
            
                while(!feof($file) and (connection_status() == 0))
                {
                    $buffer = fread($file, $chunksize);
                    echo $buffer;
                    flush();
                    
                    if($speedlimit > 0)
                        sleep(1);
                }
                
                fclose($file);
            }
            else
            {
                $index = 0;
                $speedlimit *= 1024; //convert to kb
                
                while($index < $size and (connection_status() == 0))
                {
                    $left = $size - $index;
                    $buffersize = min($left, $speedlimit);
                    
                    $buffer = substr($data, $index, $buffersize);
                    $index += $buffersize;
                    
                    echo $buffer;
                    flush();
                    sleep(1);
                }
            }
        }
        else
        {
            echo $data;
        }
    }

I hope you like this version of force_download Smile
I'm loving this framework and want to help ever I can Smile

If it's possible, I really want this version of force_download is official in some future version of CodeIgniter

Thank you all
#2

[eluser]dawnerd[/eluser]
Wow very nice. This is come in handy for sure. I just recently wrote something like this but it didn't have any of the features yours has. Thanks!
#3

[eluser]mglinski[/eluser]
Wow,
I was just about to post my own downloading library that offers the same functionality, but in a smaller package, fully commented, and very stable.

I think ill do that now, thanks for the motivation. Wink
-Matt
#4

[eluser]Donald Hughes[/eluser]
I've tried using this force_download function, the default function, and another one that someone posted and I keep getting the following error:

Code:
Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 131596288 bytes) in

These are big video files that we are making available for download, and I don't know how to force a download for files this big.


Thanks in advance for any help you guys can offer.
#5

[eluser]Wilker[/eluser]
this function enable you to cover-up this problem

please post the code where you are using this lib please, this way i can see the problem for you Wink
#6

[eluser]Donald Hughes[/eluser]
Code:
$data = file_get_contents($download_url);
force_download2($name, $data);

As you can see from the code, I added your function as "force_download2". And the code works perfectly for the smaller mp3 versions of our content.

Thanks for the help and the awesomely quick response!
#7

[eluser]Wilker[/eluser]
you are using the function in wrong way

do not pass the content direct, instead pass the file path into server, this way the function can handle the memory usage by getting small chunk pieces of file and sending Wink

correctly way:

force_download2($download_url)
#8

[eluser]Donald Hughes[/eluser]
I tried that, and now I just get a blank page. I'm a long time programmer, but I'm new to PHP (and CodeIgniter). Here's a more detailed outline of what I'm doing:

I have a link on one page:
Code:
echo '<span><a href="/sermonlibrary/download/video/high/' . $entry->id . '">Download Hi-Quality Video</a></span>';

And that link points to a controller called "Sermonlibrary" that has a function:
Code:
function download($type, $quality, $id)

Based on those parameters, it does a mySQL lookup and creates the correct url to the file. At the end I'm calling
Code:
force_download2($download_url);

After the download I was also doing:
Code:
$data['posts'] = $this->podcastmodel->getAllServicesLimit50();
$this->load->view('sermonlibrary', $data);

But it was not displaying the page correctly, so I commented it out. So now I get a blank page, and the download never starts. I echoed out the file's url and verified that the path is valid. Any other suggestions?
#9

[eluser]Wilker[/eluser]
you can pass a link to i can test this for you?

note: it can be one server configuration problem... some servers doesnt support PHP streaming... i dont remember details about how to configure php to stream at this time, but for simple a simple test, try the follow script at your server:

Code:
for($i = 0; $i < 10; $i++) {
  echo $i . '<br />';
  flush();
  sleep(1);
}

try to run the script above, if the appear 1 number per second, so your server is supporting streaming, but if the script delay to responds, and all the numbers appear at same time, your servers is not supporting streamming and the force_download with stream will not work propertly...
#10

[eluser]Donald Hughes[/eluser]
Okay, it dumped it all out at once. I use WebFaction, so I should be able to turn that on. Thank you very much for your time and your help. It is very much appreciated, as I would never have thought about it being a PHP setting. Thanks again!




Theme © iAndrew 2016 - Forum software by © MyBB