Forum Moderators: coopster

Message Too Old, No Replies

Forcing download script

This script works but still has a glitch. Help needed.

         

asantos

5:16 pm on May 31, 2006 (gmt 0)

10+ Year Member



Hi, i made this FORCE-DOWNLOAD-script taking code from some examples found on the net.

Everything works fine, but when the file is big (lets say, 4 mb), it takes a LOT of time even before starting the download process (about five seconds). I've only made tests in the local server, and i think it should be faster.

I even made a test with a 50mb file and the apache server went down, i had to restart it.


# gen_ext retrieves the file extension
# $file is the complete /path1/path2/file.ext

# Content-Type
switch(gen_ext($file)) {
case 'exe': $ctype='application/octet-stream'; break;
case 'zip': $ctype='application/zip'; break;
case 'pdf': $ctype='application/pdf'; break;
case 'doc': $ctype='application/msword'; break;
case 'xls': $ctype='application/vnd.ms-excel'; break;
case 'ppt': $ctype='application/vnd.ms-powerpoint'; break;
case 'gif': $ctype='image/gif'; break;
case 'png': $ctype='image/png'; break;
case 'jpe': case 'jpeg': case 'jpg': $ctype='image/jpg'; break;
default: $ctype='application/force-download';
}
header('Pragma: public');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Cache-Control: private',false);
header('Content-Type: '.$ctype);
header('Content-Disposition: attachment; filename='.basename($file).'');
header('Content-Transfer-Encoding: Binary');
header('Content-Length: '.filesize($file));
readfile($file);

Any ideas?

asantos

6:53 pm on May 31, 2006 (gmt 0)

10+ Year Member



I've came up with this new code, but still I have the timeout problem for big files.


$file = '/some_path1/some_path2/file.ext';

# Content-Type
switch(gen_ext(basename($file))) {
case 'exe': $ctype='application/octet-stream'; break;
case 'zip': $ctype='application/zip'; break;
case 'doc': $ctype='application/msword'; break;
case 'pdf': $ctype='application/pdf'; break;
case 'ppt': $ctype='application/vnd.ms-powerpoint'; break;
case 'xls': $ctype='application/vnd.ms-excel'; break;
case 'gif': $ctype='image/gif'; break;
case 'jpe': case 'jpeg': case 'jpg': $ctype='image/jpg'; break;
case 'png': $ctype='image/png'; break;
case 'mp3': $ctype='audio/x-mpeg, audio/x-mpeg-3, audio/mpeg3'; break;
case 'wma': $ctype='audio/x-ms-wma'; break;
case 'wmv': $ctype='video/x-ms-wmv'; break;
default: $ctype='application/force-download';
}

header('Pragma: public');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Cache-Control: private',false);
header('Content-Type: '.$ctype);
header('Content-Disposition: attachment; filename='.basename($file).'');
header('Content-Transfer-Encoding: Binary');
header('Content-Length: '.filesize($file));

# Defines memory limit. If there's no access to funcion, we use default value (2 MB)
$mem = (function_exists('memory_get_usage'))? memory_get_usage() : (2*(1024*1024));

if(filesize($file)<=$mem) {
# If filesize is less than memory limit, the file gets directly downloaded
readfile($file);
} else {
# If filesize is bigger than memory limit, the file gets downloaded in chunks
if(($f = fopen($file,'rb')) === false) die();
while (!feof($f)) {
echo fread($f,(1*(1024*1024)));
flush();
@ob_flush();
}
fclose($f);
}

ANY IDeAS?

coopster

7:45 pm on May 31, 2006 (gmt 0)

WebmasterWorld Administrator 10+ Year Member



I'm not so sure it is going to do much good to check the memory usage and compare that to the file size. [url=http:///php.net/memory_get_usage]memory_get_usage[/url] returns the amount of memory that is currently being allocated to your PHP script. Too late and too little at this point in your process, more than likely. If anything you will want to check the memory_limit [php.net] in this fashion but stream functions don't affect that configuration directive so it doesn't help anyway.

but still I have the timeout problem for big files.

What is the error message? Does set_time_limit [php.net] help?

asantos

7:48 pm on May 31, 2006 (gmt 0)

10+ Year Member



You're totally right, I replaced that memory usage stuff with this:


$mem = ini_get('memory_limit');
$mem = substr($mem,0,strpos($mem,'M'));
if(!is_numeric($mem)) {
$mem = 2*(1024*1024);
}

Still the timeout function wouldnt help, because i still need the file to get downloaded.

Ive also changed the
echo fread($f,(1*(1024*1024)));

with:
echo fread($f,1024*4);

But other problems appear to happen. For example, a video gets downloaded but the file consistency is lost and the downloaded file cant be played.

coopster

8:41 pm on May 31, 2006 (gmt 0)

WebmasterWorld Administrator 10+ Year Member



Which version of PHP are you running? There was a bug in earlier versions of PHP5 when using readfile. I had to write a chunking workaround until it was fixed in CVS. Later versions should work fine.

asantos

8:45 pm on May 31, 2006 (gmt 0)

10+ Year Member



Im using 4.4.0.

By the way, the script functions alright now. The only problem is that it takes to long to download the files (tested on local server).

For instance, cv.doc (76.5 KB) takes 9 seconds to get downloaded. The transfer rate is 8.50 KB/sec (on a local host!)

Any ideas on that? Here's the code:


# Setting para que Content-disposition funcione en IE
if(ini_get('zlib.output_compression')) ini_set('zlib.output_compression', 'Off');

# Content-Type
switch(gen_ext(basename($file))) {
case 'exe': $ctype='application/octet-stream'; break;
case 'zip': $ctype='application/zip'; break;
case 'doc': $ctype='application/msword'; break;
case 'pdf': $ctype='application/pdf'; break;
case 'ppt': $ctype='application/vnd.ms-powerpoint'; break;
case 'xls': $ctype='application/vnd.ms-excel'; break;
case 'gif': $ctype='image/gif'; break;
case 'jpe': case 'jpeg': case 'jpg': $ctype='image/jpg'; break;
case 'png': $ctype='image/png'; break;
case 'mp3': $ctype='audio/x-mpeg, audio/x-mpeg-3, audio/mpeg3'; break;
case 'wma': $ctype='audio/x-ms-wma'; break;
case 'wmv': $ctype='video/x-ms-wmv'; break;
case 'php': die(); break;
default: $ctype='application/force-download';
}

header('Pragma: public');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Cache-Control: private',false);
header('Content-Type: '.$ctype);
header('Content-Length: '.filesize($file));
header('Content-Disposition: attachment; filename="'.basename($file).'"');
header('Content-Transfer-Encoding: Binary');

@readfile($file);

What could be the slow transfer rate problem?