系统运维过程中少不了脚本编程,比如任务操持,数据爬取这些都是命令行脚本的运用领域。某日用浏览器自带的下载工具下载创造资源总是卡去世不动,而且无法重试,遂心血来潮想通过PHP脚本来自动下载文件,实现效果如下:
技能关键点
办理大文件下载内存分配不敷的问题;实现下载进度实时更新效果;命令行彩色输出。我们通过CURL来下载文件,详细初始化代码如下:
$url = 'https://www.php.net/distributions/manual/php_enhanced_zh.chm'; // 远程文件下载链接$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); http://php.net/memory-limitmemory_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/$total100).'%'.\"大众\n\公众;}
现在,你可以看到终端将会被满屏的进度数据刷屏了,很讨厌,并不是我们最顶部那张图显示的效果对不对?
赶紧关闭命令终端,我们来办理下一个问题:我们只想让进度刷新显示在一行,怎么破?这里须要复习一个冷门知识点:\r。\r也即回车,即在当前行重定向到行首,该行之前的内容将会被清空,我们只须要将progress方法中的\n更换成\r即可,便是这样大略!
echo sprintf('%.2f', $current/$total100).'%'.\"大众\r\"大众;
彩色输出下载进度
俗话说爱美之心人皆有之,我们不仅要脚本功能强大,还要它能卖得了乖。毕竟自己写的脚本自己看,轻微美化一下更能爽心悦目。关于命令终端彩色输出玩法请参考这里:https://blog.csdn.net/liunianerge/article/details/77249198,这里就不再赘述了,我们直接进入实战,还是修正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.phpclass 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('http://domain.com/path/to/file.zip'); // 想要下载点什么好呢?( ̄_, ̄ )
给你代码往期回顾:
给你代码:网站图标favicon自动抓取
给你代码:网站微信登录接入
给你代码:如何压制MySQL主键值非连续增长
给你代码:自建外贸站之PayPal支付集成
给你代码:小程序登录获取用户基本信息流程优化