Search code examples
phpdownloadfopenreadfile

fread a lot slower for downloads than readfile


I'm serving downloads from a URL to my users through a PHP script. When using readfile() I get the maximum download speed my connection can support (about 2.5MB/s) however when I use the fopen, fread, fclose route the download speed is very, very slow (about 1-2KB/s).

Here's my code:

header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename=' . $filename);
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . $filesize);
ob_clean();
flush();

$file = fopen($url, 'rb');

while(!feof($file)) {
    echo fread($file, 2014);
}

and the readfile code is simply readfile($link);.

I can't just use the readfile() function because of two reasons, one is I want to restrict the users download speed (which I can do with fread by only reading so much data) and I also want to track how much a user is downloading (I can do this with readfile() but it doesn't count partial downloads).

Does anyone know why this might be happening or how I can fix it? As far as I know readfile() is just a wrapper for fopen, fread and fclose so I don't get what's going wrong.

Edit: Ended up going with cURL for this.

$curl = curl_init();
$options = array(
    CURLOPT_URL => $rdLink,
    CURLOPT_FAILONERROR => true,
    CURLOPT_BINARYTRANSFER => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_WRITEFUNCTION => 'readResponse'
);
curl_setopt_array($curl, $options);

if(!curl_exec($curl)) {
    header('Location: http://whatever.com');
    exit;
}
curl_close($curl);

function readResponse($ch, $data) {
    $length = mb_strlen($data, '8bit');

    echo $data;

    return $length;
}

Solution

  • Use stream_context_create() and stream_get_contents()

    $context = stream_context_create();
    $file = fopen($url, 'rb', FALSE, $context);
    while(!feof($file))
    {
        //usleep(1000000);
        echo stream_get_contents($file, 2014);
    }
    

    You could also try making the file read length larger and putting in a usleep() to slow execution. The stream functions seem to be recommended over fread for the latest version of PHP anyway. You might want to also prepend an @ in front of the fread() or stream_get_contents() to suppress any errors, at least in production. Without it, and a little mishap, and you have a corrupted file.