EllisLab text mark
Advanced Search
1 of 2
1
   
force_download improved (partial download, speed limit and more)
Posted: 09 February 2008 11:53 AM
Avatar
Joined: 2008-02-09
57 posts

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:

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: ' $mimetrue);
        
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 || $data === false)
        
{
            
if($data === false)
            
{
                $chunksize 
$speedlimit $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

 Signature 

http://www.rabbitforms.com/ - a ultimate CRUD library, check forum topic at http://codeigniter.com/forums/viewthread/74393/

 
Posted: 10 February 2008 05:28 AM   [ # 1 ]   [ Rating: 0 ]
Avatar
Joined: 2008-01-12
56 posts

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!

 
Posted: 10 February 2008 03:21 PM   [ # 2 ]   [ Rating: 0 ]
Avatar
Joined: 2008-01-02
133 posts

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

 
Posted: 16 June 2008 05:21 PM   [ # 3 ]   [ Rating: 0 ]
Joined: 2008-05-09
31 posts

I’ve tried using this force_download function, the default function, and another one that someone posted and I keep getting the following error:

Fatal errorAllowed memory size of 134217728 bytes exhausted (tried to allocate 131596288 bytesin 

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.

 
Posted: 16 June 2008 05:26 PM   [ # 4 ]   [ Rating: 0 ]
Avatar
Joined: 2008-02-09
57 posts

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

 Signature 

http://www.rabbitforms.com/ - a ultimate CRUD library, check forum topic at http://codeigniter.com/forums/viewthread/74393/

 
Posted: 16 June 2008 05:38 PM   [ # 5 ]   [ Rating: 0 ]
Joined: 2008-05-09
31 posts
$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!

 
Posted: 16 June 2008 05:41 PM   [ # 6 ]   [ Rating: 0 ]
Avatar
Joined: 2008-02-09
57 posts

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)

 Signature 

http://www.rabbitforms.com/ - a ultimate CRUD library, check forum topic at http://codeigniter.com/forums/viewthread/74393/

 
Posted: 16 June 2008 06:33 PM   [ # 7 ]   [ Rating: 0 ]
Joined: 2008-05-09
31 posts

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:

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:

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

force_download2($download_url); 

After the download I was also doing:

$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?

 
Posted: 16 June 2008 06:39 PM   [ # 8 ]   [ Rating: 0 ]
Avatar
Joined: 2008-02-09
57 posts

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:

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…

 Signature 

http://www.rabbitforms.com/ - a ultimate CRUD library, check forum topic at http://codeigniter.com/forums/viewthread/74393/

 
Posted: 16 June 2008 06:43 PM   [ # 9 ]   [ Rating: 0 ]
Joined: 2008-05-09
31 posts

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!

 
Posted: 16 June 2008 08:17 PM   [ # 10 ]   [ Rating: 0 ]
Joined: 2008-05-09
31 posts

Okay, I can’t seem to figure out how to turn on output buffering so that flush() actually does something.  Anyone have any suggestions?  I’m using WebFaction, if that makes a difference.

 
Posted: 16 June 2008 08:49 PM   [ # 11 ]   [ Rating: 0 ]
Avatar
Joined: 2008-02-09
57 posts

i dont know what is webfaction XD

but, if you can setup your server, try to do this using XAMPP (http://www.apachefriends.org), the XAMPP instalation enables streaming wink

 Signature 

http://www.rabbitforms.com/ - a ultimate CRUD library, check forum topic at http://codeigniter.com/forums/viewthread/74393/

 
Posted: 16 June 2008 09:55 PM   [ # 12 ]   [ Rating: 0 ]
Joined: 2008-05-09
31 posts

Sorry, WebFaction (webfaction.com)  is a popular webhost known for being friendly to “agile” technologies like ROR and Python.  Do you have any idea what XAMPP does in it’s configuration to allow this to work?  I’m on a mac using MAMP, so I’ll give that a shot as well.  Thanks again for the help.

 
Posted: 17 June 2008 12:24 AM   [ # 13 ]   [ Rating: 0 ]
Joined: 2008-05-09
31 posts

I figured out the problem.  It had nothing to do with output buffering.  The problem was that I was passing in a full url path (http://domain/dir/file.mov), and I guess PHP’s file_exists() function does not work with full urls.  So I was getting the blank page because it was failing that check and returning false.  I commented that check out and it downloaded just fine.

 
Posted: 17 June 2008 01:02 AM   [ # 14 ]   [ Rating: 0 ]
Avatar
Joined: 2008-02-09
57 posts

you can use force_download instead of has output buffering, but has one difference:

if you do using output with streamming enable, you free memory in each cycle, but if you hasn’t streamming, all output is putted into output buffer, that accumulate memory usage of server… because of this i told you about problem without streamming wink

 Signature 

http://www.rabbitforms.com/ - a ultimate CRUD library, check forum topic at http://codeigniter.com/forums/viewthread/74393/

 
Posted: 04 February 2009 11:52 PM   [ # 15 ]   [ Rating: 0 ]
Joined: 2009-02-04
1 posts

Hello Wilker,

First of all, thanks for your code. Currently We have file upload/download script which have running on two server means server A is running the code and Server B just only store the files. When we upload a file, we placed the files to Server B through ftp and when download I used your script to download from Server B. I tried to download with IDM (Internet Download Manager), normally when I download files directly from Server B, it’s downloaded with maximum 8 connection, but when i download through my website (script), it only connected as 1 connection and others connections doesn’t work. So my download speed from script only get around maximum 20kb when comparing with direct download speed maximum 200kb. Can you please help us that how can we do to make it work ? Thanks for your time.

With Regards,

 
1 of 2
1