Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

cURL download progress in PHP

Tags:

php

curl

I'm pretty new to cURL so I've been struggling with this one for hours. I'm trying to download the source of a website in an iframe using cURL and while it's loading to show how much of it is loaded. So far I have successfully downloaded the source without showing the loading progress. Can you explain how to show the download progress? Without cURL I would read the file byte by byte and divide the total amount of downloaded bytes with the total size of the file. How can this be done in cURL since it reads the source as a whole? (at least I think that this is the only way, not sure) Here's what I've got so far:

/* Download source */
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $adress);  
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, 0); 
curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']);
$html = curl_exec($ch);
curl_close($ch); 
like image 831
Martin Avatar asked Dec 19 '12 17:12

Martin


3 Answers

What you need is

<?php
ob_start();

echo "<pre>";
echo "Loading ...";

ob_flush();
flush();

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "http://stackoverflow.com");
//curl_setopt($ch, CURLOPT_BUFFERSIZE,128);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_PROGRESSFUNCTION, 'progress');
curl_setopt($ch, CURLOPT_NOPROGRESS, false); // needed to make progress function work
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']);
$html = curl_exec($ch);
curl_close($ch);


function progress($resource,$download_size, $downloaded, $upload_size, $uploaded)
{
    if($download_size > 0)
         echo $downloaded / $download_size  * 100;
    ob_flush();
    flush();
    sleep(1); // just to see effect
}

echo "Done";
ob_flush();
flush();

?>
like image 82
Baba Avatar answered Nov 06 '22 13:11

Baba


This is how the callback looks in C:

typedef int (*curl_progress_callback)(void *clientp,
                                      double dltotal,
                                      double dlnow,
                                      double ultotal,
                                      double ulnow);

Probably in PHP it should look like

curl_progress_callback($clientp, $dltotal, $dlnow, $ultotal, $ulnow)

So, assuming you have page.html which loads a .php file in an iframe.

In your php script, you will require the following functions:

curl_setopt($curl, CURLOPT_PROGRESSFUNCTION, 'curl_progress_callback');    
curl_setopt($curl, CURLOPT_BUFFERSIZE,64000);    
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);

which should produce an output similar to the following:

0
0.1
0.2
0.2
0.3
0.4
...

then on the iframe page, you will have a progress bar

<div id="progress-bar">
    <div id="progress">0%</div>
</div>

CSS would be something like this

#progress-bar {
    width: 200px;
    padding: 2px;
    border: 2px solid #aaa;
    background: #fff;
}

#progress {
    background: #000;
    color: #fff;
    overflow: hidden;
    white-space: nowrap;
    padding: 5px 0;
    text-indent: 5px;
    width: 0%;
}

The javascript

var progressElement = document.getElementById('progress')

function updateProgress(percentage) {
    progressElement.style.width = percentage + '%';
    progressElement.innerHTML = percentage + '%';
}

You can have it output JavaScript and have it update the progress bar for you, for example:

<script>updateProgress(0);</script>
<script>updateProgress(0.1);</script>
<script>updateProgress(0.2);</script>

You might be interested in some more example code

like image 34
Kush Avatar answered Nov 06 '22 13:11

Kush


To use the callback inside a class, you must do it like this:

curl_setopt($ch, CURLOPT_PROGRESSFUNCTION, array($this, 'progress'));

or if using static functions, like this:

curl_setopt($ch, CURLOPT_PROGRESSFUNCTION, array('self', 'progress'));

... to a callback function to do whatever you need:

private static function progress($resource, $downloadSize, $downloaded, $uploadSize, $uploaded)
{
    // emit the progress
    Cache::put('download_status', [
        'resource' => $resource,
        'download_size' => $downloadSize,
        'downloaded' => $downloaded,
        'upload_size' => $uploadSize,
        'uploaded' => $uploaded
    ], 10);
}
like image 1
STWilson Avatar answered Nov 06 '22 13:11

STWilson