Jump to content

Recommended Posts

Hi all,

 

I've tried many, many variations of php download script using readfile() fread() fpassthru() etc... but none seem reliable. I can download small files perfectly fine, but say I have a 30 or 90mb zip file the download ocassionally bombs out and says the download is complete even though the full file hasn't been transferred.

I have tired it on several browsers with pretty much the same issue. The interesting bit for me is that if I download the same file across different browsers (whether or not they start at the same time or not) on my computer the download stops at the same time (not same point into download) for each browser.  Its as if the connection is being reset on my website on a global basis...

 

The important part of my script as it is at the moment:

 

// resumable download?
$is_resume = TRUE;

//Gather relevant info about file
$size = filesize($path);
$fileinfo = pathinfo($path);

@ini_set('magic_quotes_runtime', 0);
set_time_limit(0);
apache_setenv('no-gzip', '1');
mb_http_output("pass");

// required for IE, otherwise Content-disposition is ignored
if(ini_get('zlib.output_compression')) { ini_set('zlib.output_compression', 'Off'); }
	  
//workaround for IE filename bug with multiple periods / multiple dots in filename
//that adds square brackets to filename - eg. setup.abc.exe becomes setup[1].abc.exe
$filename = (strstr($_SERVER['HTTP_USER_AGENT'], 'MSIE')) ?
preg_replace('/\./', '%2e', $fileinfo['basename'], substr_count($fileinfo['basename'], '.') - 1) :
$fileinfo['basename'];

$file_extension = strtolower($fileinfo['extension']);

    //This will set the Content-Type to the appropriate setting for the file
    switch($file_extension) {
        case 'zip': $ctype='application/zip'; break;
        case 'pdf': $ctype='application/pdf'; break;
        default:    $ctype='application/force-download';
    }

    //check if http_range is sent by browser (or download manager)
    if($is_resume && isset($_SERVER['HTTP_RANGE'])) {
        list($size_unit, $range_orig) = explode('=', $_SERVER['HTTP_RANGE'], 2);
        if ($size_unit == 'bytes') {
            //multiple ranges could be specified at the same time, but for simplicity only serve the first range
            //http://tools.ietf.org/id/draft-ietf-http-range-retrieval-00.txt
            list($range, $extra_ranges) = explode(',', $range_orig, 2);
        } else {
            $range = '';
        }
    } else {
        $range = '';
    }

    //figure out download piece from range (if set)
    list($seek_start, $seek_end) = explode('-', $range, 2);

    //set start and end based on range (if set), else set defaults
    //also check for invalid ranges.
    $seek_end = (empty($seek_end)) ? ($size - 1) : min(abs(intval($seek_end)),($size - 1));
    $seek_start = (empty($seek_start) || $seek_end < abs(intval($seek_start))) ? 0 : max(abs(intval($seek_start)),0);

    //add headers if resumable
    if ($is_resume) {
        //Only send partial content header if downloading a piece of the file (IE workaround)
        if ($seek_start > 0 || $seek_end < ($size - 1)) {
            header('HTTP/1.1 206 Partial Content');
        }
	header('Accept-Ranges: bytes');
        header('Content-Range: bytes '.$seek_start.'-'.$seek_end.'/'.$size);
    }

    header("Cache-Control: cache, must-revalidate");  
    header("Pragma: public");
    header('Content-Type: ' . $ctype);
    header("Content-Disposition: attachment; filename=\"".$filename."\"");
    header('Content-Length: '.($seek_end - $seek_start + 1));

    //open the file
    $fp = fopen($path, 'rb');
    //seek to start of missing part
    fseek($fp, $seek_start);

    //start buffered download
    while(!feof($fp)) {
        //reset time limit for big files
        set_time_limit(0);
        print(fread($fp, 1024*);
        flush();
        ob_flush();
    }
    fclose($fp);
exit();

 

I did have the following section in replace of the above fread section up until this morning (but same issue):

 

// http headers for zip downloads
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-type: application/octet-stream");
header("Content-Disposition: attachment; filename=\"".$filename."\"");
header("Content-Transfer-Encoding: binary");
header('Content-Length: '.$size);
ob_end_flush();
@readfile($path);

 

I have tried different headers and so forth, but all generally seem to bomb out occassionally before the download completes. But they don't bomb out at a particular point in the file size (ie at 25mb). Just seems to work and then die at the same time across different browsers, even when they are started at different times.

 

Very strange and I've spent days modifying the script and still no answers. Sometimes the files download fine, but bomb out too many times for it to be satifactory to leave.

 

Any help or pointers would be much appreciated.

Link to comment
https://forums.phpfreaks.com/topic/248798-reliable-php-downloads/
Share on other sites

Hi all,

 

Guessing no one can help with the problem? Or suggest any ideas?

 

Anyways, I'm not sure if it is a problem with php but rather a problem with the setup of the host server? I just tried implementing the following code with the exact same results:

 

/* Execution Time Unlimited */
	set_time_limit(0);

	/*
	|----------------
	| Header
	| Forcing a download using readfile()
	|----------------
	*/

	header('Content-Description: File Transfer');
	header('Content-Type: ' . $file_mime_type);
	header('Content-Disposition: attachment; filename=' . $file);
	header('Content-Transfer-Encoding: binary');
	header('Expires: 0');
	header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
	header('Pragma: public');
	header('Content-Length: ' . $file_size);
	ob_clean();
	flush();
	readfile($file_path);
	exit;

 

I'm running out of options now, and not sure what else to pursue. Does anyone have any alternatives they could suggest? Thank you.

If you're just trying to get a file from a remote server onto your PHP webserver, I would go with ftp if you can.

 

Hi muddy,

 

No, I use cURL for that. This is a download script in a login area for some of our customers. The script is for software zip files mainly, but the occassion pdf is used. I think it has to be something wrong with the connection to the website. The files need to be secured so that they can't be stolen.

 

Is there anything else I can use to stop the connection dropping or anything other than php that is reliable and easy to use as an alternative?

if your trying to get to a client machine then PHP doesn't strike me as the best option to take, I'd look at JAVAScript or AJAX options for the big files, and use <object> containers in the HTML for the likes of PDF Files, which will allow the users to download the files or view them live using existing commercial plugins.

if your trying to get to a client machine then PHP doesn't strike me as the best option to take, I'd look at JAVAScript or AJAX options for the big files, and use <object> containers in the HTML for the likes of PDF Files, which will allow the users to download the files or view them live using existing commercial plugins.

 

The problem is that all of the files are stored outside of the www root. We don't want the software etc to be downloaded without the right permissions, and we don't want the url openly available. I don't think I can do that with <object> containers. I've never used AJAX, but that is a type of javascript isn't it? So would it fall foul of people that don't have javascript enabled browsers? If not, I'll certainly have a closer look.

 

Thanks for you comments thus far.

AJAX does require java enable browsing, bo if that's not a condition that you can set with the client machines then it's not going to be an option.  Best I can suggest is that you force the browsers download manager to take charge of the file transfer, or at the extem end, program something specific that will manage downloads in a local language like C#/++ / pascal / VB and have the end users install and use that for the files.  As for the object problem, you could simply have the tags published to the page by PHP only on event of an end user being validated.

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.