简体   繁体   中英

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;
}

Usestream_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.

It might be buffering (or perhaps even rate-limiting) somewhere in PHP or Apache. Try changing:

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

To:

while(!feof($file)) {
   $s=fread($file, 2014);
   if($s===false)break;  //Crude
   echo $s;
   @ob_flush();@flush();
}

(The @ prefix is because they might complain about empty buffers.)

As mr.freshwater said, you ought to have error-checking on your fread call, so I've added something basic above.

The reason is 2014. OS gets 4096 byte data portion very fast then others. But if you write 2014, then OS tries to read data from file by one byte to calculate it. That's why it takes so long time. Change 2014 to 4096

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM