龙空技术网

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

鲲圭外贸跨境 146

前言:

而今各位老铁们对“php保存远程文件”大概比较重视,你们都想要了解一些“php保存远程文件”的相关知识。那么小编也在网络上汇集了一些关于“php保存远程文件””的相关资讯,希望我们能喜欢,姐妹们快快来学习一下吧!

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

技术关键点

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

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

$url = ''; // 远程文件下载链接$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); 

内存分配不足解决方案

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.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(''); // 想要下载点什么好呢?( ̄_, ̄ )

给你代码往期回顾:

给你代码:网站图标favicon自动抓取

给你代码:网站微信登录接入

给你代码:如何压制MySQL主键值非连续增长

给你代码:自建外贸站之PayPal支付集成

给你代码:小程序登录获取用户基本信息流程优化

标签: #php保存远程文件