您的位置 首页 php

给你代码:PHP CLI 文件下载实时彩显进度

系统运维过程中少不了脚本编程,比如任务计划,数据爬取这些都是命令行脚本的应用领域。某日用浏览器自带的下载工具下载发现资源总是卡死不动,而且无法重试,遂心血来潮想通过PHP脚本来自动下载文件,实现效果如下:

技术关键点

  1. 解决大文件下载内存分配不足的问题;
  2. 实现下载进度实时更新效果;
  3. 命令行彩色输出。

我们通过CURL来下载文件,具体初始化代码如下:

$url = ' ch m'; // 远程文件下载链接
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); 

一般我们设置了这些选项后,就直接调用curl_exec($ch),将返回的结果保存到本地。如果下载的文件比较小,似乎是没有什么问题的,但如果文件超过了一定大小后将会报如下错误:

Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 65015808 bytes) in /path/to/download.php on line 17 

导致的原因是PHP运行脚本超过了指定的内存分配最大值,这个最大值在php.ini中可以找到:

; Maximum amount of memory a script may consume (128MB)
; 
memory_limit=128M 

内存分配不足解决方案

CURL参数设置中有个CURLOPT_FILE选项,作用是将响应结果输出到指定文件中,需要绑定到一个文件操作句柄:

$extension = (false === $pos = strrpos($url, '.')) ? '.data' : substr($url, $pos);
$fp = fopen('/path/to/'.md5(microtime()).$extension, 'wb'); // 本地文件保存路径
curl_setopt($ch, CURLOPT_NOPROGRESS, 0);
curl_setopt($ch, CURLOPT_FILE, $fp); 

下载进度实时更新

开启CURL传输进度选项,并设置回调方法:

curl_setopt($ch, CURLOPT_NOPROGRESS, true);
curl_setopt($ch, CURLOPT_PROGRESSFUNCTION, 'progress'); 

progress方法接受5个参数,后面两个参数是上传用到的,与本例无关,故忽略。我们主要看第二个和第三个参数:$total表示文件总大小,$current表示当前已下载大小,CURL会在下载过程中不断调用该方法,这样我们可以通过$current的不断变化在终端输出下载进度:

/**
 * 进度显示处理
 * @param $ch
 * @param $total 文件总大小
 * @param $current 当前已下载
 */function progress($ch, $total, $current, $_ = 0, $__ = 0) {
 if(0 == $total) return;
 echo sprintf('%.2f', $current/$total*100).'%'."\n";
} 

现在,你可以看到终端将会被满屏的进度数据刷屏了,很讨厌,并不是我们最顶部那张图显示的效果对不对?

赶紧关闭命令终端,我们来解决下一个问题:我们只想让进度刷新显示在一行,怎么破?这里需要复习一个冷门知识点:\r。\r也即回车,即在当前行重定向到行首,该行之前的内容将会被清空,我们只需要将progress方法中的\n替换成\r即可,就是这样简单!

echo sprintf('%.2f', $current/$total*100).'%'."\r"; 

彩色输出下载进度

俗话说爱美之心人皆有之,我们不仅要脚本功能强大,还要它能卖得了乖。毕竟自己写的脚本自己看,稍微美化一下更能爽心悦目。关于命令终端彩色输出玩法请参考这里:,这里就不再赘述了,我们直接进入实战,还是修改progress方法:

function progress($ch, $total, $current, $_ = 0, $__ = 0) {
 if(0 == $total) return;
 if($current > $total) $current = $total;
 $percent = $current / $total * 100;
 $percent_current = floor($percent/2);
 $percent_remain = 100/2 - $percent_current;
 echo "\033[?25l\033[42m".str_pad(' ', $percent_current)."\033[0m"
 .($percent_remain ? "\033[41m".str_pad(' ', $percent_remain)."\033[0m" : "")
 ." \033[32m".sprintf('%.2f', $percent)."%\033[0m"
 ." \033[32m".sprintf('%.2f', $current/1024/1024)."\033[0m"
 ."/\033[32m".sprintf('%.2f', $total/1024/1024)."MB\033[0m\r";
} 

这样是不是美多了?

教学阶段到此结束,现在呈上完整代码:

// download.php

class Download {
 
 static $total = 0;
 
 /**
 * 开始下载
 * @param $url 文件地址
 */ 
 static function Start($url) {
 $extension = (false === $pos = strrpos($url, '.')) ? '.data' : substr($url, $pos);
 $fp = fopen('/path/to/'.md5(microtime()).$extension, 'wb'); // 本地文件保存路径
 $ch = curl_init($url);
 curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
 curl_setopt($ch, CURLOPT_NOPROGRESS, 0);
 curl_setopt($ch, CURLOPT_FILE, $fp);
 curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
 curl_setopt($ch, CURLOPT_PROGRESSFUNCTION, 'Download::Progress');
 echo "Downloading \033[1;31m".$url."\033[0m\n";
 $response = curl_exec($ch);
 curl_close($ch);
 fclose($fp);
 self::Progress($ch, self::$total, self::$total);
 }
 
 /**
 * 进度显示处理
 * @param $ch
 * @param $total 文件总大小
 * @param $current 当前已下载
 */ static function Progress($ch, $total, $current, $_ = 0, $__ = 0) {
 if(0 == $total) return;
 self::$total = $total;
 if($current > $total) $current = $total;
 $percent = $current / $total * 100;
 $percent_current = floor($percent/2);
 $percent_remain = 100/2 - $percent_current;
 echo "\033[?25l\033[42m".str_pad(' ', $percent_current)."\033[0m"
 .($percent_remain ? "\033[41m".str_pad(' ', $percent_remain)."\033[0m" : "")
 ." \033[32m".sprintf('%.2f', $percent)."%\033[0m"
 ." \033[32m".sprintf('%.2f', $current/1024/1024)."\033[0m"
 ."/\033[32m".sprintf('%.2f', $total/1024/1024)."MB\033[0m\r";
 }
}

Download::Start(' // 想要下载点什么好呢?( ̄_, ̄ ) 

给你代码往期回顾:

文章来源:智云一二三科技

文章标题:给你代码:PHP CLI 文件下载实时彩显进度

文章地址:https://www.zhihuclub.com/77315.shtml

关于作者: 智云科技

热门文章

网站地图