PHP中的cURL下载进度

19

我对cURL非常陌生,所以我已经挣扎了几个小时。我正在尝试使用cURL下载iframe中网站的源代码,并在加载时显示加载进度。到目前为止,我已成功地下载了源代码,但没有显示加载进度。你能解释一下如何显示下载进度吗?如果不使用cURL,我会逐字节读取文件并将已下载字节数除以文件总大小。在cURL中如何实现这一点,因为它是整个源代码一次性读取的?(至少我认为这是唯一的方法,不确定)以下是我的代码:

/* 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); 

你是在终端还是网页浏览器中执行脚本?如果是在网页浏览器中,你需要使用JavaScript。 - Kush
在网页浏览器中 - 在 iframe 中打印网页源代码 - Martin
我相信这个基于PHP的下载加速器 this 是你想要的。 - user2465095
4个回答

31

你需要的是

<?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();

?>

1
它运行良好,但是在进度函数中存在一堆错误,其中包括除以零的情况。也许是当下载大小为0时? - Martin
注意:ob_flush() [ref.outcontrol]: 刷新缓冲区失败。没有可刷新的缓冲区。在C:\Users\mr.v\my sites_vlab\test-php-progress-bar\test1.php第7行。注意:ob_flush() [ref.outcontrol]: 刷新缓冲区失败。没有可刷新的缓冲区。在C:\Users\mr.v\my sites_vlab\test-php-progress-bar\test1.php第26行。有什么问题吗? - vee

7
这是C语言中回调函数的样例:
typedef int (*curl_progress_callback)(void *clientp,
                                      double dltotal,
                                      double dlnow,
                                      double ultotal,
                                      double ulnow);

在PHP中,它应该看起来像这样:

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

假设您有一个名为page.html的文件,它在iframe中加载了一个.php文件。
在您的php脚本中,您需要使用以下函数:
curl_setopt($curl, CURLOPT_PROGRESSFUNCTION, 'curl_progress_callback');    
curl_setopt($curl, CURLOPT_BUFFERSIZE,64000);    
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);

应该生成类似以下输出的结果:
0
0.1
0.2
0.2
0.3
0.4
...

然后在iframe页面上,你将会看到一个进度条。

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

CSS的内容可能如下所示:
#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%;
}

JavaScript(简称JS)
var progressElement = document.getElementById('progress')

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

你可以让它输出JavaScript并为你更新进度条,例如:

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

您可能对一些更多的示例代码感兴趣


为什么回调函数有不同的名称? 我认为 curl_setopt($curl, CURLOPT_PROGRESSFUNCTION, 'callback'); 应该改为 curl_setopt($curl, CURLOPT_PROGRESSFUNCTION, 'curl_progress_callback'); 对吗? - Martin

1

如果要在类内使用回调,必须这样做:

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

或者如果使用静态函数,可以这样写:

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

将其传递给回调函数,以执行您需要的任何操作:
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);
}

0

我的Web服务器刷新出现了问题。我通过添加以下内容解决了这个问题:

    for ($j = 0; $j < 128; $j++) {
        print "<!---------------------------->\n";
    }

在 flush() 之前,执行 ob_flush()。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接