在之前的文章中,我们分享过一个好用的php 多线程下载类,本节再为大家介绍一个php的多线程类,不过这个是通过curl来实现的。
有兴趣的朋友,可以把两篇文章作个对比,看看其中的不同与各自的长处在哪里?
代码如下:
<?php /* * @ php Curl 多线程类 * edit by www. */ class MultiHttpRequest{ public $urls=array(); public $curlopt_header=1; public $method="GET"; function __construct($urls=false){ $this->urls=$urls; } function set_urls($urls){ $this->urls=$urls; return $this; } function is_return_header($b){ $this->curlopt_header=$b; return $this; } function set_method($m){ $this->medthod=strtoupper($m); return $this; } function start(){ if(!is_array($this->urls) || count($this->urls)==0){ return false; } $curl=$text=array(); $handle=curl_multi_init(); foreach($this->urls as $k=>$v){ $curl[$k]=$this->add_handle($handle,$v); } $this->exec_handle($handle); foreach($this->urls as $k=>$v){ curl_multi_getcontent($curl[$k]); echo $curl[$k]."\n"; //$text[$k]= curl_multi_getcontent($curl[$k]); //echo $text[$k],"\n\n"; curl_multi_remove_handle($handle,$curl[$k]); } curl_multi_close($handle); } private function add_handle($handle,$url){ $curl=curl_init(); curl_setopt($curl,CURLOPT_URL,$url); curl_setopt($curl,CURLOPT_HEADER,$this->curlopt_header); curl_setopt($curl,CURLOPT_RETURNTRANSFER,1); curl_multi_add_handle($handle,$curl); return $curl; } private function exec_handle($handle){ $flag=null; do{ curl_multi_exec($handle,$flag); } while ($flag > 0); } } ?>
调用示例
<?php $urls=array( 'http://www./article/665.html', 'http://www./article/392.html', 'http://www./article/281.html'); $mp=new MultiHttpRequest($urls); $mp->start(); ?>
有关php中curl的相关内容,请参考如下文章:
php curl上传文件
php开启curl扩展
php curl post的简单示例
php curl应用实例
php curl用法实例代码
php curl 学习总结
多线程下载类,原生php代码实现,分享给大家。
代码如下:
<?php
/**
* @author 野马
* 多进程下载类
* 主要针对curl的多进程下载进行的封装。
* 使用代码:
<?php
include "multi_download.php";
$xml = file_get_contents('./download.xml');
preg_match_all('/<string>(http.*)<\/string>/', $xml, $rs);
$md = new multi_download();
foreach( $rs[1] as $line ) {
$filepath = "./file/" . basename($line);
$md->addtask( $line, $filepath );
}
$md->dotask();
?>
*/
class multi_download {
/**
* 下载队列
* @var array
* array(
* array(
* 'handle' => curl resource
* 'filepath' => '/tmp/abc.png'
* 'url' => 'http://www./logo.png'
* 'info' => curl_getinfo
* ),
* )
*/
private $threads = array();
/**
* curl批处理句柄
* @var resource
*/
private $multi_hand = NULL;
/**
* 活动进程指针
* @var array
*/
private $active_points = array();
/**
* 下一个要激活的进程指针
* @var integer
*/
private $active_next = 0;
/**
* 最多活动进程数
* @var integer
*/
private $active_max = 5;
/**
* 模拟浏览器发送agent
*/
private $user_agent = "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)";
/**
* 构造方法
*/
public function __construct() {
$this->multi_hand = curl_multi_init();
}
/**
* 添加下载任务
* @param string $url 下载地址
* @param string $filepath 文件存放路径,全路径
* @return void
*/
public function addtask( $url, $filepath ) {
$this->threads[] = array(
'handle' => NULL,
'url' => $url,
'filepath' => $filepath,
'info' => array()
);
}
/**
* 执行下载任务
*/
public function dotask() {
while( TRUE ) {
$viewScreen = '';
// 处理每一个进程的状态,清理已完成进程
foreach( $this->active_points as $vk => $point ) {
curl_multi_exec( $this->multi_hand, $active );
$thr = &$this->threads[ $point ];
$thr[ 'info' ] = curl_getinfo( $thr[ 'handle' ] );
// 组织当前进程的回显内容。
$viewScreen .= $this->viewStatus( $thr, $point );
// 下载完成,释放资源
if( $thr[ 'info' ]['http_code'] != 0 && $thr[ 'info' ][ 'size_download' ] == $thr[ 'info' ][ 'download_content_length' ] ) {
curl_multi_remove_handle( $this->multi_hand, $thr[ 'handle' ] );
unset( $this->active_points[ $vk ] );
}
}
// 启动进程
if( $this->active_max - count( $this->active_points ) > 0 ) for(
$i = 0, $num = $this->active_max - count( $this->active_points );
$i < $num && !empty( $this->threads[ $this->active_next ] ) ;
$i++, $this->active_next++
) {
$thr = &$this->threads[ $this->active_next ];
$thr[ 'handle' ] = $this->createconn( $thr[ 'url' ], $thr[ 'filepath' ] );
curl_multi_add_handle ( $this->multi_hand, $thr[ 'handle' ] );
curl_multi_exec( $this->multi_hand, $active );
$this->active_points[] = $this->active_next;
$viewScreen .= $this->viewAddThread( $thr, $this->active_next );
}
// 显示当前状态。
echo `clear`;
echo $viewScreen;
if( empty( $this->threads[ $this->active_next ] ) && empty( $this->active_points ) ) {
echo "\r\nAll downloaded.\r\n";
break;
}
// 间歇1秒
sleep( 1 );
}
}
/**
* 格式化进程回显信息。
* @param array $thr 进程数组
* @param ineteger $point 进程ID
* @return string
* @edit www.
*/
private function viewStatus( &$thr, $point ) {
if( empty( $thr[ 'info' ] ) ) return false;
$return = "thread({$point}): ";
if( $thr[ 'info' ]['http_code'] == 0 ) {
return $return . "start.\r\n";
}
elseif( $thr[ 'info' ][ 'size_download' ] == $thr[ 'info' ][ 'download_content_length' ] ) {
$return .= "finish. ";
}
else {
$return .= "active. ";
}
$return .= "[use time:" . intval( $thr[ 'info' ][ 'total_time' ] ) . ",";// ]\r\n";
$return .= " speed:" . $this->formateSize( $thr[ 'info' ][ 'speed_download' ] ) . ",";
$return .= " download:" . ( intval( $thr[ 'info' ][ 'size_download' ] * 100 / $thr[ 'info' ][ 'download_content_length' ] ) ) . "%(" . $this->formateSize( $thr[ 'info' ][ 'size_download' ] ) . ")";
$return .= " file size:" . $this->formateSize( $thr[ 'info' ][ 'download_content_length' ] );
$return .= "]\r\n";
return $return;
}
/**
* 格式化进程回显信息。
* @param array $thr 进程数组
* @param ineteger $point 进程ID
* @return string
*/
private function viewAddThread( &$thr, $point ) {
return "thread({$point}): initialize.\r\n";
}
/**
* 格式化文件大小
* @param integer $size 文件字节数
* @return string
* @edit www.
*/
public function formateSize($size) {
$units = array('B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB');
$unitindex = 0;
while($size > 1024) {
$size /= 1024;
++$unitindex;
}
return sprintf("%0.2f", $size) . $units[ $unitindex ];
}
/**
* 创建curl连接
* @param string $url 下载地址
* @param string $filepath 文件存放路径,全路径
* @return curl resource
*/
public function createconn( $url, $filepath ) {
$fp = fopen ( $filepath, "w" );
$conn = curl_init( $url );
curl_setopt( $conn, CURLOPT_USERAGENT, $this->user_agent );
curl_setopt( $conn, CURLOPT_FILE, $fp );
curl_setopt( $conn, CURLOPT_HEADER, 0 );
curl_setopt( $conn, CURLOPT_CONNECTTIMEOUT, 60 );
return $conn;
}
}
?>
本节跟大家分享下php中全局变量$_SERVER['PATH_INFO']及pathinfo()函数的用法,就我所了解的当前流行的cms系统,比如dedecms与帝国cms均多有用到这二个,来定义或获取全局信息。
来看个例子,有如下的网址:
http://www./index.php/index/do.html?c=index&m=search
则,
$_SERVER['QUERY_STRING'] = 'c=index&m=search';
这里给出一个解析PATH_INFO的代码:
<?php /** * pathinfo应用举例 * edit by www. */ if( !isset( $_SERVER['PATH_INFO'] ) ){ $pathinfo = 'default'; }else{ $pathinfo = explode('/', $_SERVER['PATH_INFO']); } if( is_array($pathinfo) AND !empty($pathinfo) ){ $page = $pathinfo[1]; }else{ $page = 'a.php'; } require "$page.php"; ?>
下面再来看一个例子,这里介绍的不再是$_SERVER['PATH_INFO']全局变量了,而是我们今天的主角pathinfo函数。
pathinfo()函数
pathinfo()函数返回一个包含了文件信息的数组,数组中有四个元素,分别是dirname、basename、extension、filename。
例如,有如下的文件路径,可以用pathinfo进行分解:
<?php //pathinfo函数用法 $path = "/www/xxx/images/logo.jpg"; $fileArr = pathinfo($path); print_r($fileArr); //输出结果:Array ( [dirname] => /www/xxx/images [basename] => logo.jpg [extension] => jpg [filename] => logo ) //根据数组的键名就可以获得对应的键值 echo $fileArr['filename']; //输出结果:logo echo $fileArr['extension']; //输出结果:jpg ?>
最后,说说pathinfo()函数在处理中文时遇到的问题及解决方法。
问题描述:
pathinfo在处理中文文件名时,如果中文在字首就出现获取的filename为空,英文在字首后面是中文的则能获取到。
如下图所示:
可以用如下的函数取代它,代码如下:
<?php //增强型的pathinfo函数 //edit by www. function path_info($filepath) { $path_parts = array(); $path_parts ['dirname'] = rtrim(substr($filepath, 0, strrpos($filepath, '/')),"/")."/"; $path_parts ['basename'] = ltrim(substr($filepath, strrpos($filepath, '/')),"/"); $path_parts ['extension'] = substr(strrchr($filepath, '.'), 1); $path_parts ['filename'] = ltrim(substr($path_parts ['basename'], 0, strrpos($path_parts ['basename'], '.')),"/"); return $path_parts; } ?>
用这个函数去处理中文文件路径或文件时,就不会有问题了。
如下图所示: