├── .gitignore ├── app.conf ├── .htaccess ├── views ├── welcome_leaf.php └── welcome.php ├── README.md ├── include ├── storages │ ├── bcs │ │ ├── conf.inc.php │ │ ├── libs │ │ │ └── requestcore │ │ │ │ ├── README.md │ │ │ │ ├── LICENSE │ │ │ │ └── requestcore.class.php │ │ ├── utils │ │ │ └── mimetypes.class.php │ │ └── bcs.class.php │ ├── oss │ │ ├── conf.inc.php │ │ ├── lib │ │ │ └── requestcore │ │ │ │ ├── README.md │ │ │ │ ├── LICENSE │ │ │ │ └── requestcore.class.php │ │ ├── lang │ │ │ └── zh.inc.php │ │ └── util │ │ │ └── mimetypes.class.php │ ├── Sae.php │ ├── Bae.php │ ├── Memcached.php │ ├── Oss.php │ ├── Local.php │ └── Gcs.php ├── storage.php ├── view.php ├── start.php ├── lib.php └── controller.php ├── config.yaml ├── config.sample.inc.php └── index.php /.gitignore: -------------------------------------------------------------------------------- 1 | /cache 2 | /config.inc.php 3 | -------------------------------------------------------------------------------- /app.conf: -------------------------------------------------------------------------------- 1 | handlers: 2 | - check_exist: not_exist 3 | script: /index.php -------------------------------------------------------------------------------- /.htaccess: -------------------------------------------------------------------------------- 1 | 2 | RewriteEngine On 3 | RewriteBase / 4 | RewriteRule ^index\.php$ - [L] 5 | RewriteCond %{REQUEST_FILENAME} !-f 6 | RewriteCond %{REQUEST_FILENAME} !-d 7 | RewriteRule . /index.php [L] 8 | -------------------------------------------------------------------------------- /views/welcome_leaf.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## LayerLocalCDN 2 | 3 | forked from [LayerCDN](https://github.com/oott123/Layer). 4 | 5 | LayerLocalCDN由LayerCDN修改而来。 6 | 7 | LayerLocalCDN只专注于本地PHP加速的方式。 8 | 9 | 目前支持平台:SAE(新浪)、BAE(百度)、GCS(盛大)、标准PHP(本地读写) 10 | 11 | 您可以访问支持网站来获取帮助:http://www.wordpressleaf.com 12 | 13 | 最后更新: 14 | 2016.7.24 修正了没有指定缓存目录,导致恶意访问时会生成杂乱目录的问题。 15 | 16 | 17 | -------------------------------------------------------------------------------- /include/storages/bcs/conf.inc.php: -------------------------------------------------------------------------------- 1 | instance = new SaeStorage(SAE_ACCESSKEY,SAE_SECRETKEY); 14 | } 15 | 16 | } -------------------------------------------------------------------------------- /include/storages/oss/conf.inc.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /include/start.php: -------------------------------------------------------------------------------- 1 | . You can get the code from . 12 | 13 | ### License and Copyright 14 | 15 | This code is Copyright (c) 2008-2010, Ryan Parman. However, I'm licensing this code for others to use under the [Simplified BSD license](http://www.opensource.org/licenses/bsd-license.php). 16 | -------------------------------------------------------------------------------- /include/storages/bcs/libs/requestcore/README.md: -------------------------------------------------------------------------------- 1 | # RequestCore 2 | 3 | RequestCore is a lightweight cURL-based HTTP request/response class that leverages MultiCurl for parallel requests. 4 | 5 | ### PEAR HTTP_Request? 6 | 7 | RequestCore was written as a replacement for [PEAR HTTP_Request](http://pear.php.net/http_request/). While PEAR HTTP_Request is full-featured and heavy, RequestCore features only the essentials and is very lightweight. It also leverages the batch request support in cURL's `curl_multi_exec()` to enable multi-threaded requests that fire in parallel. 8 | 9 | ### Reference and Download 10 | 11 | You can find the class reference at . You can get the code from . 12 | 13 | ### License and Copyright 14 | 15 | This code is Copyright (c) 2008-2010, Ryan Parman. However, I'm licensing this code for others to use under the [Simplified BSD license](http://www.opensource.org/licenses/bsd-license.php). 16 | -------------------------------------------------------------------------------- /include/storages/Sae.php: -------------------------------------------------------------------------------- 1 | domain = DOMAIN; 16 | $this->instance = new SaeStorage(SAE_ACCESSKEY,SAE_SECRETKEY); 17 | } 18 | 19 | public function exists($filename){ 20 | return $this->instance->fileExists($this->domain,$filename); 21 | } 22 | //这里是效率瓶颈啊!! 23 | public function read($filename){ 24 | return $this->instance->read($this->domain,$filename); 25 | } 26 | 27 | public function write($name,$content){ 28 | return $this->instance->write($this->domain,$name,$content); 29 | } 30 | 31 | public function url($name){ 32 | return $this->instance->getUrl($this->domain,$name); 33 | } 34 | 35 | public function error(){ 36 | return $this->instance->error(); 37 | } 38 | 39 | public function delete($name){ 40 | return $this->instance->delete($this->domain,$name); 41 | } 42 | 43 | } -------------------------------------------------------------------------------- /include/storages/Bae.php: -------------------------------------------------------------------------------- 1 | domain = DOMAIN; 13 | require dirname(__FILE__).'/bcs/bcs.class.php'; 14 | $this->instance = new BaiduBCS(); 15 | } 16 | 17 | public function exists($filename){ 18 | return $this->instance->is_object_exist($this->domain,$this->get_file($filename)); 19 | } 20 | //这里是效率瓶颈啊!! 21 | public function read($filename){ 22 | return $this->instance->get_object($this->domain,$this->get_file($filename)); 23 | } 24 | 25 | public function write($name,$content){ 26 | return $this->instance->create_object_by_content($this->domain,$this->get_file($name),$content); 27 | } 28 | 29 | public function url($name){ 30 | //return $this->instance->getUrl($this->domain,$this->get_file($name)); 31 | return 'http://bcs.duapp.com/'.$this->domain.$this->get_file($name); 32 | } 33 | 34 | public function error(){ 35 | return false; 36 | } 37 | 38 | public function delete($name){ 39 | return $this->instance->delete_object($this->domain,$this->get_file($name)); 40 | } 41 | 42 | private function get_file($name){ 43 | return '/'.ltrim($name,'/'); 44 | } 45 | } -------------------------------------------------------------------------------- /include/storages/Memcached.php: -------------------------------------------------------------------------------- 1 | instance = new BaeMemcache(); 14 | }elseif(class_exists('Memcache')){ 15 | $this->instance = new Memcache(); 16 | if(method_exists('Memcache','init')){ 17 | $this->instance->init(); 18 | }else{ 19 | $this->instance->connect(defined(CS_AK)?CS_AK:'127.0.0.1',defined(CS_SK)?CS_SK:'11211'); 20 | } 21 | }else{ 22 | die('No memcache.'); 23 | } 24 | } 25 | 26 | public function exists($filename){ 27 | return $this->instance->get($this->get_file($filename)); 28 | } 29 | public function read($filename){ 30 | return $this->instance->get($this->get_file($filename)); 31 | } 32 | 33 | public function write($name,$content){ 34 | return $this->instance->set($this->get_file($name),$content); 35 | } 36 | 37 | public function url($name){ 38 | return false; 39 | } 40 | 41 | public function error(){ 42 | return false; 43 | } 44 | 45 | public function delete($name){ 46 | return $this->instance->delete($this->get_file($name)); 47 | } 48 | 49 | private function get_file($name){ 50 | return md5($name); 51 | } 52 | } -------------------------------------------------------------------------------- /config.sample.inc.php: -------------------------------------------------------------------------------- 1 | domain = DOMAIN; 15 | require dirname(__FILE__).'/oss/sdk.class.php'; 16 | $this->instance = new ALIOSS(); 17 | $this->headurl = 'http://oss.aliyuncs.com/'.DOMAIN.'/'; 18 | } 19 | 20 | public function exists($filename){ 21 | $res=$this->instance->is_object_exist($this->domain,$this->get_file($filename)); 22 | if($res->status==404){ 23 | return false; 24 | } 25 | return true; 26 | } 27 | public function read($filename){ 28 | $bucket = $this->domain; 29 | $object = $this->get_file($filename); 30 | 31 | $options = array( 32 | //ALIOSS::OSS_FILE_DOWNLOAD => "d:\\cccccccccc.sh", 33 | //ALIOSS::OSS_CONTENT_TYPE => 'txt/html', 34 | ); 35 | 36 | $response = $this->instance->get_object($bucket,$object,$options); 37 | return $contents; 38 | } 39 | 40 | public function write($name,$content){ 41 | $object = $this->get_file($name); 42 | $upload_file_options = array( 43 | 'content' => $content, 44 | 'length' => strlen($content), 45 | ALIOSS::OSS_HEADERS => array( 46 | //'Expires' => '2012-10-01 08:00:00', 47 | ), 48 | ); 49 | 50 | $response = $this->instance->upload_file_by_content($this->domain,$object,$upload_file_options); 51 | } 52 | 53 | public function url($name){ 54 | return $this->headurl.$this->get_file($name); 55 | } 56 | 57 | public function error(){ 58 | return false; 59 | } 60 | 61 | public function delete($name){ 62 | return $this->instance->delete_object($this->domain,$this->get_file($name)); 63 | } 64 | 65 | private function get_file($name){ 66 | return ltrim($name,'/'); 67 | } 68 | } -------------------------------------------------------------------------------- /include/lib.php: -------------------------------------------------------------------------------- 1 | setHeader('Host',STATIC_HOST); 27 | } 28 | $content = $f->fetch($url); 29 | return array($f->HttpCode(),$content); 30 | break; 31 | case 'BAE': 32 | case 'LOCAL': 33 | default: 34 | if(function_exists('curl_init')){ 35 | //BAE或普通平台下可使用curl 36 | $ch = curl_init(); 37 | curl_setopt($ch, CURLOPT_URL, $url); 38 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 39 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 40 | curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); 41 | if(!ini_get('safe_mode')){ 42 | curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); 43 | } 44 | if(STATIC_HOST){ 45 | curl_setopt($ch, CURLOPT_HTTPHEADER, array('Host: '.STATIC_HOST)); 46 | } 47 | $con = curl_exec($ch); 48 | $cod = curl_getinfo($ch,CURLINFO_HTTP_CODE); 49 | return array($cod,$con); 50 | }else{ 51 | //否则使用file_get_contents 52 | $content = ''; 53 | if(STATIC_HOST){ 54 | $opt=array('http'=>array('header'=>'Host: '.STATIC_HOST)); 55 | $context=stream_context_create($opt); 56 | $content = file_get_contents($url,false,$context); 57 | }else{ 58 | $content = file_get_contents($url); 59 | } 60 | list($version,$status_code,$msg) = explode(' ',$http_response_header[0], 3); 61 | return array($status_code,$content); 62 | } 63 | } 64 | } 65 | 66 | } -------------------------------------------------------------------------------- /include/storages/Local.php: -------------------------------------------------------------------------------- 1 | data_dir = $data_dir.'/'; 12 | } 13 | public function exists($filename){ 14 | return is_file($this->get_file($filename)); 15 | } 16 | public function read($filename){ 17 | return file_get_contents($this->get_file($filename)); 18 | } 19 | public function write($filename,$content){ 20 | return file_put_contents($this->get_file($filename),$content); 21 | } 22 | public function url($filename){ 23 | //return false; //不提供URL方式读取 24 | //return rtrim(DOMAIN,'/').'/'.$this->get_file($filename,false); 25 | 26 | //应该从网站url根开始 27 | return '/'.rtrim(DOMAIN,'/').'/'.$this->get_file($filename,false); 28 | } 29 | public function delete($filename){ 30 | return unlink($this->get_file($filename)); 31 | } 32 | public function error(){ 33 | return false; 34 | } 35 | private function get_file($key,$pre = true){ 36 | if(NO_KEY || NO_SECOND_FLODER){ 37 | $dir = dirname($this->data_dir.$key); 38 | if(!is_dir($dir)){ 39 | if(!mkdir($dir,0777,true)) die(json_encode(array('error'=>'cannot_make_dir'))); 40 | } 41 | if(!$pre) return $key; 42 | 43 | return $this->data_dir.$key; //如果是url跳转方式,那么返回带路径文件名。 44 | //应该是空间的绝对路径加上缓存路径 45 | // /leaf/ 加上 wp-content/themes/wordpressleaf/style.css 46 | 47 | } 48 | $letter1 = substr($key,0,1); 49 | $letter2 = substr($key,0,2); 50 | $dir = $this->data_dir.$letter1.'/'.$letter2; 51 | if(!is_dir($dir)){ 52 | if(!mkdir($dir,0777,true)){ 53 | if(!$pre) return $key; 54 | 55 | 56 | return $this->data_dir.$key; 57 | } 58 | } 59 | if(!$pre) return $letter1.'/'.$letter2.'/'.$key; 60 | return $dir.'/'.$key; 61 | } 62 | } -------------------------------------------------------------------------------- /include/storages/Gcs.php: -------------------------------------------------------------------------------- 1 | domain = DOMAIN; 13 | require dirname(__FILE__).'/gcs/GrandCloudStorage.php'; 14 | $this->instance = new GrandCloudStorage('http://storage.grandcloud.cn'); 15 | $this->instance->set_key_secret(CS_AK, CS_SK); 16 | $this->instance->set_bucket(DOMAIN); 17 | $this->headurl = 'http://storage-'. 18 | $this->instance->head_bucket(DOMAIN). 19 | '.sdcloud.cn'; 20 | $this->instance->set_host($this->headurl); 21 | } 22 | 23 | public function exists($filename){ 24 | //GCS木有正常的检测文件是否存在的API囧,那就获取信息吧,失败就不存在 25 | try{ 26 | $this->instance->head_object($this->get_file($filename)); 27 | return true; 28 | }catch(Exception $e){ 29 | return false; 30 | } 31 | } 32 | public function read($filename){ 33 | //GCS你是要闹哪样啊摔 34 | $temp = tmpfile(); 35 | $this->instance->get_object($this->get_file($filename), $temp); 36 | fseek($temp,0); 37 | $contents = ""; 38 | while (!feof($temp)){ 39 | $contents .= fread($temp,8192); 40 | } 41 | return $contents; 42 | } 43 | 44 | public function write($name,$content){ 45 | //同上啊摔 46 | $temp = tmpfile(); 47 | fwrite($temp,$content); 48 | fseek($temp,0); 49 | //$temp = tempnam(sys_get_temp_dir()); 50 | //file_put_contents($temp,$content); 51 | $this->instance->put_object($this->get_file($name), $temp); 52 | //unlink($temp); 53 | } 54 | 55 | public function url($name){ 56 | return $this->headurl.$this->instance->get_object_resource($this->get_file($name),30*24*60*60); 57 | } 58 | 59 | public function error(){ 60 | return false; 61 | } 62 | 63 | public function delete($name){ 64 | return $this->instance->delete_object($this->get_file($name)); 65 | } 66 | 67 | private function get_file($name){ 68 | return ltrim($name,'/'); 69 | } 70 | } -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | '', 57 | 'REQUEST_URI' => '', 58 | ); 59 | 60 | $_SERVER = array_merge( $default_server_values, $_SERVER ); 61 | 62 | // Fix for IIS when running with PHP ISAPI 63 | if ( empty( $_SERVER['REQUEST_URI'] ) || ( php_sapi_name() != 'cgi-fcgi' && preg_match( '/^Microsoft-IIS\//', $_SERVER['SERVER_SOFTWARE'] ) ) ) { 64 | 65 | // IIS Mod-Rewrite 66 | if ( isset( $_SERVER['HTTP_X_ORIGINAL_URL'] ) ) { 67 | $_SERVER['REQUEST_URI'] = $_SERVER['HTTP_X_ORIGINAL_URL']; 68 | } 69 | // IIS Isapi_Rewrite 70 | else if ( isset( $_SERVER['HTTP_X_REWRITE_URL'] ) ) { 71 | $_SERVER['REQUEST_URI'] = $_SERVER['HTTP_X_REWRITE_URL']; 72 | } else { 73 | // Use ORIG_PATH_INFO if there is no PATH_INFO 74 | if ( !isset( $_SERVER['PATH_INFO'] ) && isset( $_SERVER['ORIG_PATH_INFO'] ) ) 75 | $_SERVER['PATH_INFO'] = $_SERVER['ORIG_PATH_INFO']; 76 | 77 | // Some IIS + PHP configurations puts the script-name in the path-info (No need to append it twice) 78 | if ( isset( $_SERVER['PATH_INFO'] ) ) { 79 | if ( $_SERVER['PATH_INFO'] == $_SERVER['SCRIPT_NAME'] ) 80 | $_SERVER['REQUEST_URI'] = $_SERVER['PATH_INFO']; 81 | else 82 | $_SERVER['REQUEST_URI'] = $_SERVER['SCRIPT_NAME'] . $_SERVER['PATH_INFO']; 83 | } 84 | 85 | // Append the query string if it exists and isn't null 86 | if ( ! empty( $_SERVER['QUERY_STRING'] ) ) { 87 | $_SERVER['REQUEST_URI'] .= '?' . $_SERVER['QUERY_STRING']; 88 | } 89 | } 90 | } 91 | 92 | // Fix for PHP as CGI hosts that set SCRIPT_FILENAME to something ending in php.cgi for all requests 93 | if ( isset( $_SERVER['SCRIPT_FILENAME'] ) && ( strpos( $_SERVER['SCRIPT_FILENAME'], 'php.cgi' ) == strlen( $_SERVER['SCRIPT_FILENAME'] ) - 7 ) ) 94 | $_SERVER['SCRIPT_FILENAME'] = $_SERVER['PATH_TRANSLATED']; 95 | 96 | // Fix for Dreamhost and other PHP as CGI hosts 97 | if ( strpos( $_SERVER['SCRIPT_NAME'], 'php.cgi' ) !== false ) 98 | unset( $_SERVER['PATH_INFO'] ); 99 | 100 | // Fix empty PHP_SELF 101 | $PHP_SELF = $_SERVER['PHP_SELF']; 102 | if ( empty( $PHP_SELF ) ) 103 | $_SERVER['PHP_SELF'] = $PHP_SELF = preg_replace( '/(\?.*)?$/', '', $_SERVER["REQUEST_URI"] ); 104 | 105 | 106 | require_once BASE_PATH.'include/start.php'; -------------------------------------------------------------------------------- /include/storages/oss/lang/zh.inc.php: -------------------------------------------------------------------------------- 1 | 'application/vnd.android.package-archive', 10 | '3gp' => 'video/3gpp', 'ai' => 'application/postscript', 11 | 'aif' => 'audio/x-aiff', 'aifc' => 'audio/x-aiff', 12 | 'aiff' => 'audio/x-aiff', 'asc' => 'text/plain', 13 | 'atom' => 'application/atom+xml', 'au' => 'audio/basic', 14 | 'avi' => 'video/x-msvideo', 'bcpio' => 'application/x-bcpio', 15 | 'bin' => 'application/octet-stream', 'bmp' => 'image/bmp', 16 | 'cdf' => 'application/x-netcdf', 'cgm' => 'image/cgm', 17 | 'class' => 'application/octet-stream', 18 | 'cpio' => 'application/x-cpio', 19 | 'cpt' => 'application/mac-compactpro', 20 | 'csh' => 'application/x-csh', 'css' => 'text/css', 21 | 'dcr' => 'application/x-director', 'dif' => 'video/x-dv', 22 | 'dir' => 'application/x-director', 'djv' => 'image/vnd.djvu', 23 | 'djvu' => 'image/vnd.djvu', 24 | 'dll' => 'application/octet-stream', 25 | 'dmg' => 'application/octet-stream', 26 | 'dms' => 'application/octet-stream', 27 | 'doc' => 'application/msword', 'dtd' => 'application/xml-dtd', 28 | 'dv' => 'video/x-dv', 'dvi' => 'application/x-dvi', 29 | 'dxr' => 'application/x-director', 30 | 'eps' => 'application/postscript', 'etx' => 'text/x-setext', 31 | 'exe' => 'application/octet-stream', 32 | 'ez' => 'application/andrew-inset', 'flv' => 'video/x-flv', 33 | 'gif' => 'image/gif', 'gram' => 'application/srgs', 34 | 'grxml' => 'application/srgs+xml', 35 | 'gtar' => 'application/x-gtar', 'gz' => 'application/x-gzip', 36 | 'hdf' => 'application/x-hdf', 37 | 'hqx' => 'application/mac-binhex40', 'htm' => 'text/html', 38 | 'html' => 'text/html', 'ice' => 'x-conference/x-cooltalk', 39 | 'ico' => 'image/x-icon', 'ics' => 'text/calendar', 40 | 'ief' => 'image/ief', 'ifb' => 'text/calendar', 41 | 'iges' => 'model/iges', 'igs' => 'model/iges', 42 | 'jnlp' => 'application/x-java-jnlp-file', 'jp2' => 'image/jp2', 43 | 'jpe' => 'image/jpeg', 'jpeg' => 'image/jpeg', 44 | 'jpg' => 'image/jpeg', 'js' => 'application/x-javascript', 45 | 'kar' => 'audio/midi', 'latex' => 'application/x-latex', 46 | 'lha' => 'application/octet-stream', 47 | 'lzh' => 'application/octet-stream', 48 | 'm3u' => 'audio/x-mpegurl', 'm4a' => 'audio/mp4a-latm', 49 | 'm4p' => 'audio/mp4a-latm', 'm4u' => 'video/vnd.mpegurl', 50 | 'm4v' => 'video/x-m4v', 'mac' => 'image/x-macpaint', 51 | 'man' => 'application/x-troff-man', 52 | 'mathml' => 'application/mathml+xml', 53 | 'me' => 'application/x-troff-me', 'mesh' => 'model/mesh', 54 | 'mid' => 'audio/midi', 'midi' => 'audio/midi', 55 | 'mif' => 'application/vnd.mif', 'mov' => 'video/quicktime', 56 | 'movie' => 'video/x-sgi-movie', 'mp2' => 'audio/mpeg', 57 | 'mp3' => 'audio/mpeg', 'mp4' => 'video/mp4', 58 | 'mpe' => 'video/mpeg', 'mpeg' => 'video/mpeg', 59 | 'mpg' => 'video/mpeg', 'mpga' => 'audio/mpeg', 60 | 'ms' => 'application/x-troff-ms', 'msh' => 'model/mesh', 61 | 'mxu' => 'video/vnd.mpegurl', 'nc' => 'application/x-netcdf', 62 | 'oda' => 'application/oda', 'ogg' => 'application/ogg', 63 | 'ogv' => 'video/ogv', 'pbm' => 'image/x-portable-bitmap', 64 | 'pct' => 'image/pict', 'pdb' => 'chemical/x-pdb', 65 | 'pdf' => 'application/pdf', 66 | 'pgm' => 'image/x-portable-graymap', 67 | 'pgn' => 'application/x-chess-pgn', 'pic' => 'image/pict', 68 | 'pict' => 'image/pict', 'png' => 'image/png', 69 | 'pnm' => 'image/x-portable-anymap', 70 | 'pnt' => 'image/x-macpaint', 'pntg' => 'image/x-macpaint', 71 | 'ppm' => 'image/x-portable-pixmap', 72 | 'ppt' => 'application/vnd.ms-powerpoint', 73 | 'ps' => 'application/postscript', 'qt' => 'video/quicktime', 74 | 'qti' => 'image/x-quicktime', 'qtif' => 'image/x-quicktime', 75 | 'ra' => 'audio/x-pn-realaudio', 76 | 'ram' => 'audio/x-pn-realaudio', 'ras' => 'image/x-cmu-raster', 77 | 'rdf' => 'application/rdf+xml', 'rgb' => 'image/x-rgb', 78 | 'rm' => 'application/vnd.rn-realmedia', 79 | 'roff' => 'application/x-troff', 'rtf' => 'text/rtf', 80 | 'rtx' => 'text/richtext', 'sgm' => 'text/sgml', 81 | 'sgml' => 'text/sgml', 'sh' => 'application/x-sh', 82 | 'shar' => 'application/x-shar', 'silo' => 'model/mesh', 83 | 'sit' => 'application/x-stuffit', 84 | 'skd' => 'application/x-koan', 'skm' => 'application/x-koan', 85 | 'skp' => 'application/x-koan', 'skt' => 'application/x-koan', 86 | 'smi' => 'application/smil', 'smil' => 'application/smil', 87 | 'snd' => 'audio/basic', 'so' => 'application/octet-stream', 88 | 'spl' => 'application/x-futuresplash', 89 | 'src' => 'application/x-wais-source', 90 | 'sv4cpio' => 'application/x-sv4cpio', 91 | 'sv4crc' => 'application/x-sv4crc', 'svg' => 'image/svg+xml', 92 | 'swf' => 'application/x-shockwave-flash', 93 | 't' => 'application/x-troff', 'tar' => 'application/x-tar', 94 | 'tcl' => 'application/x-tcl', 'tex' => 'application/x-tex', 95 | 'texi' => 'application/x-texinfo', 96 | 'texinfo' => 'application/x-texinfo', 'tif' => 'image/tiff', 97 | 'tiff' => 'image/tiff', 'tr' => 'application/x-troff', 98 | 'tsv' => 'text/tab-separated-values', 'txt' => 'text/plain', 99 | 'ustar' => 'application/x-ustar', 100 | 'vcd' => 'application/x-cdlink', 'vrml' => 'model/vrml', 101 | 'vxml' => 'application/voicexml+xml', 'wav' => 'audio/x-wav', 102 | 'wbmp' => 'image/vnd.wap.wbmp', 103 | 'wbxml' => 'application/vnd.wap.wbxml', 'webm' => 'video/webm', 104 | 'wml' => 'text/vnd.wap.wml', 105 | 'wmlc' => 'application/vnd.wap.wmlc', 106 | 'wmls' => 'text/vnd.wap.wmlscript', 107 | 'wmlsc' => 'application/vnd.wap.wmlscriptc', 108 | 'wmv' => 'video/x-ms-wmv', 'wrl' => 'model/vrml', 109 | 'xbm' => 'image/x-xbitmap', 'xht' => 'application/xhtml+xml', 110 | 'xhtml' => 'application/xhtml+xml', 111 | 'xls' => 'application/vnd.ms-excel', 112 | 'xml' => 'application/xml', 'xpm' => 'image/x-xpixmap', 113 | 'xsl' => 'application/xml', 'xslt' => 'application/xslt+xml', 114 | 'xul' => 'application/vnd.mozilla.xul+xml', 115 | 'xwd' => 'image/x-xwindowdump', 'xyz' => 'chemical/x-xyz', 116 | 'zip' => 'application/zip' ); 117 | 118 | public static function get_mimetype($ext) { 119 | return (isset ( self::$mime_types [$ext] ) ? self::$mime_types [$ext] : 'application/octet-stream'); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /views/welcome.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Layer CDN 8 | 9 | 10 | 88 | 89 | 90 |
91 |

LayerLocal CDN

92 |
93 |
LayerLocal CDN 已经成功安装!
94 |
95 |

关于LayerLocal

96 |
97 |

LayerLocal是一个基于PHP的轻巧的CDN程序,你可以利用LayerLocal快速地为你的博客或者网站搭建自己的CDN,从而加速网站的加载,提升用户体验。

98 |

Layer由SaeLayerCDN修改而来。

99 |

LayerLocal由LayerCDN修改而来。

100 |

相比SaeLayerCDN,新版Layer的优势在于:

101 |
    102 |
  • 基于SAE/BAE以及本地可写PHP环境,程序免费开源。
  • 103 |
  • 可扩展性高,可轻易移植到任何云平台。
  • 104 |
  • 防盗链保护,更省流量。
  • 105 |
  • URL刷新缓存,轻松维护。
  • 106 |
  • 自定义HTTP Host、自定义缓存后缀,极端特殊情况也能轻松使用,方便进行全站CDN。
  • 107 |
  • 内存缓存,更高效,更易管理。(开发中)
  • 108 |
109 |

相比LayerCDN,新版LayerLocal的优势在于:

110 |
    111 |
  • 修正本地PHP环境读写Bug,程序继续免费开源。
  • 112 |
  • 本地PHP环境支持带问号的静态文件。
  • 113 |
  • 本地PHP环境增加四种mime type。
  • 114 |
  • 本地PHP环境支持url跳转和read读取两种方式。
  • 115 |
116 |
117 |
118 |
119 |

安装和使用

120 |
121 |
    122 |
  • 部署代码:可以上传代码包或通过git/svn部署。
  • 123 |
  • 配置程序:如果是手动部署,则要先在SAE后台创建一个storage的domain,或者在BAE后台创建一个bucket,然后右键点击bucket设为公开读属性。然后设置index.php,修改下面的三个常量即可使用(其它特性请自行设置,有详细注解): 124 |
  • 145 |
  • 使用CDN:将网站模板中静态文件的的根目录URL替换成你的SAE/BAE/PHP应用地址,如将http://blog.creatist.cn/logo.jpg换成http://mysqecdn.sinaapp.com/logo.jpg。对于网站程序的一个建议是,设置一个$cdn_base配置变量,然后静态文件的URL根据$cdn_base生成,以后修改CDN只要配置这个变量就可以了。
  • 146 |
  • 最后,刷新你的网站。然后感受网站加载速度的飞跃。
  • 147 |
148 |

149 |
150 |
151 |
152 |

原理

153 |
154 |

大致就是取静态内容的过程中增加一个CDN层:前端从CDN取静态文件,当该文件是第一次被访问时,CDN从源服务器上取文件并保存到自己的storage里,然后返回给前端;之后就直接从CDN取而不需要再访问源服务器了。这样的好处就是在源服务器端可以实现无痛切换,不用使用像又拍CDN之类的API进行专门的编程,用户资源仍保存在源服务器上,只要将静态资源的前缀改为CDN的网址就行了,当不想使用CDN时可以再改回来。

例如,源文件地址是http://www.creatist.cn/avatar/21223.jpg(或者相对地址/avatar/21223.jpg),前端使用CDN,访问地址改成http://cdnxxx.sinaapp.com/avatar/21223.jpg(这通常是非常容易修改的)。CDN首先检查/avatar/21223.jpg在storage中是否已存在,如果存在即以前已经被访问过的话就直接取出来;如果不存在就从http://www.creatist.cn/avatar/21223.jpg保存到storage,以后就直接从storage里面取了。

CDN层还能实现其他很多功能,例如设置浏览器缓存、防盗链等等。

155 |
156 |
157 |
158 |

其他

159 |
160 |

Layer还在完善中,项目的Github地址是:https://github.com/oott123/Layer,欢迎fork和贡献代码。

161 |

SaeLayerCDN的作者的博客是:http://blog.creatist.cn/

162 |

163 |
164 |

LayerLocalCDN说明

165 |
166 |

LayerLocalCDN只专注于本地PHP加速的方式,项目的Github地址是:https://github.com/yehaicao/LayerLocalCDN,欢迎fork和贡献代码。

167 |

如果你有其他建议和问题,可以访问LayerLocalCDN的支持网站是:http://www.wordpressleaf.com/

168 |

注意:LayerLocal只检测并修正了本地目录+文件的加速方式。本地MD5文件方式未进行测试,如果有问题请前来反馈。

169 |

170 |
171 |
172 |
173 |
174 | 175 | -------------------------------------------------------------------------------- /include/storages/bcs/utils/mimetypes.class.php: -------------------------------------------------------------------------------- 1 | 'video/3gpp', 'ai' => 'application/postscript', 5 | 'aif' => 'audio/x-aiff', 'aifc' => 'audio/x-aiff', 6 | 'aiff' => 'audio/x-aiff', 'asc' => 'text/plain', 7 | 'atom' => 'application/atom+xml', 'au' => 'audio/basic', 8 | 'avi' => 'video/x-msvideo', 'bcpio' => 'application/x-bcpio', 9 | 'bin' => 'application/octet-stream', 'bmp' => 'image/bmp', 10 | 'cdf' => 'application/x-netcdf', 'cgm' => 'image/cgm', 11 | 'class' => 'application/octet-stream', 12 | 'cpio' => 'application/x-cpio', 13 | 'cpt' => 'application/mac-compactpro', 14 | 'csh' => 'application/x-csh', 'css' => 'text/css', 15 | 'dcr' => 'application/x-director', 'dif' => 'video/x-dv', 16 | 'dir' => 'application/x-director', 'djv' => 'image/vnd.djvu', 17 | 'djvu' => 'image/vnd.djvu', 18 | 'dll' => 'application/octet-stream', 19 | 'dmg' => 'application/octet-stream', 20 | 'dms' => 'application/octet-stream', 21 | 'doc' => 'application/msword', 'dtd' => 'application/xml-dtd', 22 | 'dv' => 'video/x-dv', 'dvi' => 'application/x-dvi', 23 | 'dxr' => 'application/x-director', 24 | 'eps' => 'application/postscript', 'etx' => 'text/x-setext', 25 | 'exe' => 'application/octet-stream', 26 | 'ez' => 'application/andrew-inset', 'flv' => 'video/x-flv', 27 | 'gif' => 'image/gif', 'gram' => 'application/srgs', 28 | 'grxml' => 'application/srgs+xml', 29 | 'gtar' => 'application/x-gtar', 'gz' => 'application/x-gzip', 30 | 'hdf' => 'application/x-hdf', 31 | 'hqx' => 'application/mac-binhex40', 'htm' => 'text/html', 32 | 'html' => 'text/html', 'ice' => 'x-conference/x-cooltalk', 33 | 'ico' => 'image/x-icon', 'ics' => 'text/calendar', 34 | 'ief' => 'image/ief', 'ifb' => 'text/calendar', 35 | 'iges' => 'model/iges', 'igs' => 'model/iges', 36 | 'jnlp' => 'application/x-java-jnlp-file', 'jp2' => 'image/jp2', 37 | 'jpe' => 'image/jpeg', 'jpeg' => 'image/jpeg', 38 | 'jpg' => 'image/jpeg', 'js' => 'application/x-javascript', 39 | 'kar' => 'audio/midi', 'latex' => 'application/x-latex', 40 | 'lha' => 'application/octet-stream', 41 | 'lzh' => 'application/octet-stream', 42 | 'm3u' => 'audio/x-mpegurl', 'm4a' => 'audio/mp4a-latm', 43 | 'm4p' => 'audio/mp4a-latm', 'm4u' => 'video/vnd.mpegurl', 44 | 'm4v' => 'video/x-m4v', 'mac' => 'image/x-macpaint', 45 | 'man' => 'application/x-troff-man', 46 | 'mathml' => 'application/mathml+xml', 47 | 'me' => 'application/x-troff-me', 'mesh' => 'model/mesh', 48 | 'mid' => 'audio/midi', 'midi' => 'audio/midi', 49 | 'mif' => 'application/vnd.mif', 'mov' => 'video/quicktime', 50 | 'movie' => 'video/x-sgi-movie', 'mp2' => 'audio/mpeg', 51 | 'mp3' => 'audio/mpeg', 'mp4' => 'video/mp4', 52 | 'mpe' => 'video/mpeg', 'mpeg' => 'video/mpeg', 53 | 'mpg' => 'video/mpeg', 'mpga' => 'audio/mpeg', 54 | 'ms' => 'application/x-troff-ms', 'msh' => 'model/mesh', 55 | 'mxu' => 'video/vnd.mpegurl', 'nc' => 'application/x-netcdf', 56 | 'oda' => 'application/oda', 'ogg' => 'application/ogg', 57 | 'ogv' => 'video/ogv', 'pbm' => 'image/x-portable-bitmap', 58 | 'pct' => 'image/pict', 'pdb' => 'chemical/x-pdb', 59 | 'pdf' => 'application/pdf', 60 | 'pgm' => 'image/x-portable-graymap', 61 | 'pgn' => 'application/x-chess-pgn', 'pic' => 'image/pict', 62 | 'pict' => 'image/pict', 'png' => 'image/png', 63 | 'pnm' => 'image/x-portable-anymap', 64 | 'pnt' => 'image/x-macpaint', 'pntg' => 'image/x-macpaint', 65 | 'ppm' => 'image/x-portable-pixmap', 66 | 'ppt' => 'application/vnd.ms-powerpoint', 67 | 'ps' => 'application/postscript', 'qt' => 'video/quicktime', 68 | 'qti' => 'image/x-quicktime', 'qtif' => 'image/x-quicktime', 69 | 'ra' => 'audio/x-pn-realaudio', 70 | 'ram' => 'audio/x-pn-realaudio', 'ras' => 'image/x-cmu-raster', 71 | 'rdf' => 'application/rdf+xml', 'rgb' => 'image/x-rgb', 72 | 'rm' => 'application/vnd.rn-realmedia', 73 | 'roff' => 'application/x-troff', 'rtf' => 'text/rtf', 74 | 'rtx' => 'text/richtext', 'sgm' => 'text/sgml', 75 | 'sgml' => 'text/sgml', 'sh' => 'application/x-sh', 76 | 'shar' => 'application/x-shar', 'silo' => 'model/mesh', 77 | 'sit' => 'application/x-stuffit', 78 | 'skd' => 'application/x-koan', 'skm' => 'application/x-koan', 79 | 'skp' => 'application/x-koan', 'skt' => 'application/x-koan', 80 | 'smi' => 'application/smil', 'smil' => 'application/smil', 81 | 'snd' => 'audio/basic', 'so' => 'application/octet-stream', 82 | 'spl' => 'application/x-futuresplash', 83 | 'src' => 'application/x-wais-source', 84 | 'sv4cpio' => 'application/x-sv4cpio', 85 | 'sv4crc' => 'application/x-sv4crc', 'svg' => 'image/svg+xml', 86 | 'swf' => 'application/x-shockwave-flash', 87 | 't' => 'application/x-troff', 'tar' => 'application/x-tar', 88 | 'tcl' => 'application/x-tcl', 'tex' => 'application/x-tex', 89 | 'texi' => 'application/x-texinfo', 90 | 'texinfo' => 'application/x-texinfo', 'tif' => 'image/tiff', 91 | 'tiff' => 'image/tiff', 'tr' => 'application/x-troff', 92 | 'tsv' => 'text/tab-separated-values', 'txt' => 'text/plain', 93 | 'ustar' => 'application/x-ustar', 94 | 'vcd' => 'application/x-cdlink', 'vrml' => 'model/vrml', 95 | 'vxml' => 'application/voicexml+xml', 'wav' => 'audio/x-wav', 96 | 'wbmp' => 'image/vnd.wap.wbmp', 97 | 'wbxml' => 'application/vnd.wap.wbxml', 'webm' => 'video/webm', 98 | 'wml' => 'text/vnd.wap.wml', 99 | 'wmlc' => 'application/vnd.wap.wmlc', 100 | 'wmls' => 'text/vnd.wap.wmlscript', 101 | 'wmlsc' => 'application/vnd.wap.wmlscriptc', 102 | 'wmv' => 'video/x-ms-wmv', 'wrl' => 'model/vrml', 103 | 'xbm' => 'image/x-xbitmap', 'xht' => 'application/xhtml+xml', 104 | 'xhtml' => 'application/xhtml+xml', 105 | 'xls' => 'application/vnd.ms-excel', 106 | 'xml' => 'application/xml', 'xpm' => 'image/x-xpixmap', 107 | 'xsl' => 'application/xml', 'xslt' => 'application/xslt+xml', 108 | 'xul' => 'application/vnd.mozilla.xul+xml', 109 | 'xwd' => 'image/x-xwindowdump', 'xyz' => 'chemical/x-xyz', 110 | 'zip' => 'application/zip', 111 | //add by zhengkan 20110905 112 | "apk" => "application/vnd.android.package-archive", 113 | "bin" => "application/octet-stream", 114 | "cab" => "application/vnd.ms-cab-compressed", 115 | "gb" => "application/chinese-gb", 116 | "gba" => "application/octet-stream", 117 | "gbc" => "application/octet-stream", 118 | "jad" => "text/vnd.sun.j2me.app-descriptor", 119 | "jar" => "application/java-archive", 120 | "nes" => "application/octet-stream", 121 | "rar" => "application/x-rar-compressed", 122 | "sis" => "application/vnd.symbian.install", 123 | "sisx" => "x-epoc/x-sisx-app", 124 | "smc" => "application/octet-stream", 125 | "smd" => "application/octet-stream", 126 | "swf" => "application/x-shockwave-flash", 127 | "zip" => "application/x-zip-compressed", 128 | "wap" => "text/vnd.wap.wml wml", "mrp" => "application/mrp", 129 | //add by zhengkan 20110914 130 | "wma" => "audio/x-ms-wma", 131 | "lrc" => "application/lrc" ); 132 | public static function get_mimetype($ext) { 133 | $ext = strtolower ( $ext ); 134 | return (isset ( self::$mime_types [$ext] ) ? self::$mime_types [$ext] : 'application/octet-stream'); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /include/controller.php: -------------------------------------------------------------------------------- 1 | content_type = 'text/html'; 18 | $this->error_type = 0; 19 | $this->succeed = TRUE; 20 | 21 | $request = ltrim($request,'/'); 22 | $ext = 'cache'; 23 | 24 | $request = strpos($request,'?') ? substr($request,0,strpos($request,'?')) : $request ; 25 | 26 | 27 | 28 | //检测环境 29 | if(!RUN_ENV){ 30 | $this->error_type = 'no_run_env'; 31 | $this->succeed = FALSE; 32 | } 33 | 34 | //请求为空 35 | elseif($request === '' && WELCOME_DOC){ 36 | //显示欢迎页面 37 | view::show('welcome'); 38 | return ; 39 | } 40 | else{ 41 | //检查防盗链 42 | $referer = isset($_SERVER['HTTP_REFERER'])?$_SERVER['HTTP_REFERER']:''; 43 | @$referer = parse_url($referer); 44 | $referer = isset($referer['host'])?$referer['host']:''; 45 | if(ALLOW_REGX && !preg_match('/'.ALLOW_REGX.'/i',$referer)){ 46 | $this->error_type = 'not_allowed_domain'; 47 | $this->succeed = FALSE; 48 | }else{ 49 | //匹配文件后缀 50 | $mime_types = array( 51 | '3gp' => 'video/3gpp', 52 | 'ai' => 'application/postscript', 53 | 'aif' => 'audio/x-aiff', 54 | 'aifc' => 'audio/x-aiff', 55 | 'aiff' => 'audio/x-aiff', 56 | 'asc' => 'text/plain', 57 | 'atom' => 'application/atom+xml', 58 | 'au' => 'audio/basic', 59 | 'avi' => 'video/x-msvideo', 60 | 'bcpio' => 'application/x-bcpio', 61 | 'bin' => 'application/octet-stream', 62 | 'bmp' => 'image/bmp', 63 | 'cdf' => 'application/x-netcdf', 64 | 'cgm' => 'image/cgm', 65 | 'class' => 'application/octet-stream', 66 | 'cpio' => 'application/x-cpio', 67 | 'cpt' => 'application/mac-compactpro', 68 | 'csh' => 'application/x-csh', 69 | 'css' => 'text/css', 70 | 'dcr' => 'application/x-director', 71 | 'dif' => 'video/x-dv', 72 | 'dir' => 'application/x-director', 73 | 'djv' => 'image/vnd.djvu', 74 | 'djvu' => 'image/vnd.djvu', 75 | 'dll' => 'application/octet-stream', 76 | 'dmg' => 'application/octet-stream', 77 | 'dms' => 'application/octet-stream', 78 | 'doc' => 'application/msword', 79 | 'dtd' => 'application/xml-dtd', 80 | 'dv' => 'video/x-dv', 81 | 'dvi' => 'application/x-dvi', 82 | 'dxr' => 'application/x-director', 83 | 'eps' => 'application/postscript', 84 | 'etx' => 'text/x-setext', 85 | 'exe' => 'application/octet-stream', 86 | 'ez' => 'application/andrew-inset', 87 | 'flv' => 'video/x-flv', 88 | 'gif' => 'image/gif', 89 | 'gram' => 'application/srgs', 90 | 'grxml' => 'application/srgs+xml', 91 | 'gtar' => 'application/x-gtar', 92 | 'gz' => 'application/x-gzip', 93 | 'hdf' => 'application/x-hdf', 94 | 'hqx' => 'application/mac-binhex40', 95 | 'htm' => 'text/html', 96 | 'html' => 'text/html', 97 | 'ice' => 'x-conference/x-cooltalk', 98 | 'ico' => 'image/x-icon', 99 | 'ics' => 'text/calendar', 100 | 'ief' => 'image/ief', 101 | 'ifb' => 'text/calendar', 102 | 'iges' => 'model/iges', 103 | 'igs' => 'model/iges', 104 | 'jnlp' => 'application/x-java-jnlp-file', 105 | 'jp2' => 'image/jp2', 106 | 'jpe' => 'image/jpeg', 107 | 'jpeg' => 'image/jpeg', 108 | 'jpg' => 'image/jpeg', 109 | 'js' => 'application/x-javascript', 110 | 'kar' => 'audio/midi', 111 | 'latex' => 'application/x-latex', 112 | 'lha' => 'application/octet-stream', 113 | 'lzh' => 'application/octet-stream', 114 | 'm3u' => 'audio/x-mpegurl', 115 | 'm4a' => 'audio/mp4a-latm', 116 | 'm4p' => 'audio/mp4a-latm', 117 | 'm4u' => 'video/vnd.mpegurl', 118 | 'm4v' => 'video/x-m4v', 119 | 'mac' => 'image/x-macpaint', 120 | 'man' => 'application/x-troff-man', 121 | 'mathml' => 'application/mathml+xml', 122 | 'me' => 'application/x-troff-me', 123 | 'mesh' => 'model/mesh', 124 | 'mid' => 'audio/midi', 125 | 'midi' => 'audio/midi', 126 | 'mif' => 'application/vnd.mif', 127 | 'mov' => 'video/quicktime', 128 | 'movie' => 'video/x-sgi-movie', 129 | 'mp2' => 'audio/mpeg', 130 | 'mp3' => 'audio/mpeg', 131 | 'mp4' => 'video/mp4', 132 | 'mpe' => 'video/mpeg', 133 | 'mpeg' => 'video/mpeg', 134 | 'mpg' => 'video/mpeg', 135 | 'mpga' => 'audio/mpeg', 136 | 'ms' => 'application/x-troff-ms', 137 | 'msh' => 'model/mesh', 138 | 'mxu' => 'video/vnd.mpegurl', 139 | 'nc' => 'application/x-netcdf', 140 | 'oda' => 'application/oda', 141 | 'ogg' => 'application/ogg', 142 | 'ogv' => 'video/ogv', 143 | 'pbm' => 'image/x-portable-bitmap', 144 | 'pct' => 'image/pict', 145 | 'pdb' => 'chemical/x-pdb', 146 | 'pdf' => 'application/pdf', 147 | 'pgm' => 'image/x-portable-graymap', 148 | 'pgn' => 'application/x-chess-pgn', 149 | 'pic' => 'image/pict', 150 | 'pict' => 'image/pict', 151 | 'png' => 'image/png', 152 | 'pnm' => 'image/x-portable-anymap', 153 | 'pnt' => 'image/x-macpaint', 154 | 'pntg' => 'image/x-macpaint', 155 | 'ppm' => 'image/x-portable-pixmap', 156 | 'ppt' => 'application/vnd.ms-powerpoint', 157 | 'ps' => 'application/postscript', 158 | 'qt' => 'video/quicktime', 159 | 'qti' => 'image/x-quicktime', 160 | 'qtif' => 'image/x-quicktime', 161 | 'ra' => 'audio/x-pn-realaudio', 162 | 'ram' => 'audio/x-pn-realaudio', 163 | 'ras' => 'image/x-cmu-raster', 164 | 'rdf' => 'application/rdf+xml', 165 | 'rgb' => 'image/x-rgb', 166 | 'rm' => 'application/vnd.rn-realmedia', 167 | 'roff' => 'application/x-troff', 168 | 'rtf' => 'text/rtf', 169 | 'rtx' => 'text/richtext', 170 | 'sgm' => 'text/sgml', 171 | 'sgml' => 'text/sgml', 172 | 'sh' => 'application/x-sh', 173 | 'shar' => 'application/x-shar', 174 | 'silo' => 'model/mesh', 175 | 'sit' => 'application/x-stuffit', 176 | 'skd' => 'application/x-koan', 177 | 'skm' => 'application/x-koan', 178 | 'skp' => 'application/x-koan', 179 | 'skt' => 'application/x-koan', 180 | 'smi' => 'application/smil', 181 | 'smil' => 'application/smil', 182 | 'snd' => 'audio/basic', 183 | 'so' => 'application/octet-stream', 184 | 'spl' => 'application/x-futuresplash', 185 | 'src' => 'application/x-wais-source', 186 | 'sv4cpio' => 'application/x-sv4cpio', 187 | 'sv4crc' => 'application/x-sv4crc', 188 | 'svg' => 'image/svg+xml', 189 | 'swf' => 'application/x-shockwave-flash', 190 | 't' => 'application/x-troff', 191 | 'tar' => 'application/x-tar', 192 | 'tcl' => 'application/x-tcl', 193 | 'tex' => 'application/x-tex', 194 | 'texi' => 'application/x-texinfo', 195 | 'texinfo' => 'application/x-texinfo', 196 | 'tif' => 'image/tiff', 197 | 'tiff' => 'image/tiff', 198 | 'tr' => 'application/x-troff', 199 | 'tsv' => 'text/tab-separated-values', 200 | 'txt' => 'text/plain', 201 | 'ustar' => 'application/x-ustar', 202 | 'vcd' => 'application/x-cdlink', 203 | 'vrml' => 'model/vrml', 204 | 'vxml' => 'application/voicexml+xml', 205 | 'wav' => 'audio/x-wav', 206 | 'wbmp' => 'image/vnd.wap.wbmp', 207 | 'wbxml' => 'application/vnd.wap.wbxml', 208 | 'webm' => 'video/webm', 209 | 'wml' => 'text/vnd.wap.wml', 210 | 'wmlc' => 'application/vnd.wap.wmlc', 211 | 'wmls' => 'text/vnd.wap.wmlscript', 212 | 'wmlsc' => 'application/vnd.wap.wmlscriptc', 213 | 'wmv' => 'video/x-ms-wmv', 214 | 'wrl' => 'model/vrml', 215 | 'xbm' => 'image/x-xbitmap', 216 | 'xht' => 'application/xhtml+xml', 217 | 'xhtml' => 'application/xhtml+xml', 218 | 'xls' => 'application/vnd.ms-excel', 219 | 'xml' => 'application/xml', 220 | 'xpm' => 'image/x-xpixmap', 221 | 'xsl' => 'application/xml', 222 | 'xslt' => 'application/xslt+xml', 223 | 'xul' => 'application/vnd.mozilla.xul+xml', 224 | 'xwd' => 'image/x-xwindowdump', 225 | 'xyz' => 'chemical/x-xyz', 226 | 'zip' => 'application/zip', 227 | 'eot' => 'application/vnd.ms-fontobject', 228 | 'ttf' => 'font/ttf', 229 | 'otf' => 'font/ttf', 230 | 'woff' => 'application/x-font-woff', 231 | 'woff2' => 'application/font-woff2', 232 | ); 233 | $basename = basename($request); 234 | /* 235 | basename() 236 | 获取文件名部分 237 | $path = "/testweb/home.php"; 238 | 239 | //显示带有文件扩展名的文件名 240 | echo basename($path); 241 | 242 | 243 | 244 | //显示不带有文件扩展名的文件名 245 | echo basename($path,".php"); 246 | 247 | 输出结果 248 | home.php 249 | home 250 | 251 | */ 252 | 253 | $ext = strtolower(substr($basename,strrpos($basename,'.')+1)); 254 | 255 | 256 | //解决有带问号的扩展名导致文件类型错误的问题,查找问号第一次出现的位置。 257 | 258 | 259 | 260 | 261 | $ext_qm = strpos($ext,'?') ? substr($ext,0,strpos($ext,'?')) : $ext ; 262 | 263 | 264 | 265 | /* 266 | //获取扩展名, 267 | strrpos() 268 | 在文件名中找到 "." 的位置,将位置加1,往后挪动一位。 269 | substr() 270 | 截取位置之后的字符 271 | strtolower() 272 | 将字符转换为小写。 273 | 274 | 275 | 276 | */ 277 | 278 | 279 | 280 | if(isset($mime_types[$ext_qm])){ 281 | $this->content_type=$mime_types[$ext_qm]; 282 | } 283 | /* 284 | 利用扩展名变量 285 | 286 | 从开始定义的文件类型中,为$this->content_type赋值 287 | 288 | */ 289 | 290 | 291 | $direct = false; 292 | 293 | 294 | if(in_array($ext,explode('|',strtolower(DIRECT_EXT)))){ 295 | $direct = true; 296 | } 297 | /* 298 | 判断$ext扩展名是否在开始定义的不缓存的类型中。 299 | 300 | */ 301 | 302 | } 303 | } 304 | 305 | //开始处理 306 | $delete = false; 307 | if(count($purge = explode(PURGE_KEY.'/',$request,2))>1){ 308 | $delete = true; 309 | $request = $purge[1]; 310 | } 311 | //调用函数,检查是不是指定的缓存目录 312 | 313 | $this->cdn_dir($request); 314 | 315 | $key = (NO_KEY) ? $request:md5($request).'_'.strlen($request).'.'.$ext; 316 | $this->hit = false; 317 | $this->handle($request,$key,$delete,$direct); 318 | 319 | } 320 | /** 321 | * 获取内容并输出 322 | * 如果stroage里面不存在,则从URL里面获取 323 | * */ 324 | private function handle($filename,$key,$delete = false,$direct = false){ 325 | $content = ''; 326 | if($this->succeed){ 327 | $storage = storage::gethandle(); 328 | if($delete){ 329 | if(!$storage->exists($key)){ 330 | die(json_encode(array('purge'=>$filename,'key'=>$key,'success'=>'not exists'))); 331 | } 332 | $return = $storage->delete($key); 333 | die(json_encode(array('purge'=>$filename,'key'=>$key,'success'=>$return))); 334 | } 335 | if($storage->exists($key) && !$direct){ 336 | if(!NO_LOCATE && $url = $storage->url($key)){ 337 | $this->locate($url); 338 | } 339 | $content = $storage->read($key); 340 | $this->hit = $key; 341 | if(empty($content)){ 342 | $this->succeed = false; 343 | $this->error_type = 'empty_conent'; 344 | } 345 | }else{ 346 | //$content = @file_get_contents(BASE_URL.$filename); 347 | $content = lib::fetch_url(BASE_URL.$filename); 348 | if(!is_array($content) || count($content)<2){ 349 | $this->succeed = false; 350 | $this->error_type = 'fetch_error'; 351 | }elseif($content[0]==200){ 352 | //返回200,才写入 353 | if(!$direct) $storage->write($key, $content[1]); 354 | }else{ 355 | header('HTTP/1.1 '.$content[0]); 356 | } 357 | $content = $content[1]; 358 | } 359 | } 360 | //显示内容 361 | $this->render($content); 362 | } 363 | 364 | 365 | /** 366 | * 输出结果,包括缓存控制等 367 | * */ 368 | private function render($content=''){ 369 | ob_end_clean(); 370 | if(!$this->succeed){ 371 | $this->error(); 372 | return ; 373 | }else{ 374 | if($this->hit){ 375 | header('Layer-Cache: Hit;key='.$this->hit.';ENV='.RUN_ENV); 376 | }else{ 377 | header('Layer-Cache: Miss;ENV='.RUN_ENV); 378 | } 379 | header("Expires: " . date("D, j M Y H:i:s GMT", time()+2592000));//缓存一月 380 | header('Content-type: '.$this->content_type); 381 | echo $content; 382 | } 383 | } 384 | 385 | private function locate($url){ 386 | //302 387 | header("HTTP/1.1 302 Moved Temporarily"); 388 | header("Location:".$url); 389 | die(); 390 | } 391 | 392 | /** 393 | * 处理错误 394 | * */ 395 | private function error(){ 396 | $this->content_type = 'text/html'; 397 | echo json_encode(array('error'=>$this->error_type)); 398 | } 399 | 400 | 401 | private function cdn_dir($request){ 402 | 403 | /******目录检测开始 ********/ 404 | 405 | //判断请求的URL是不是规定的目录 406 | 407 | 408 | 409 | //将状态设为0 410 | 411 | $cdn_dirs_staus=0; 412 | 413 | //查找是否存在'/',有则意味这有目录,进入缓存目录判断,如果没有,则意味着是根目录下的,可以直接缓存。 414 | 415 | if ( stripos($request,'/') ) { 416 | 417 | 418 | 419 | if (CDN_DIR){ //判断是否有预设的缓存目录 420 | 421 | //先将可以缓存的目录解析为数组 422 | 423 | $cdn_dirs=explode('|',strtolower(CDN_DIR)); 424 | 425 | //查找请求的URL是否在预设的缓存目录里面 426 | 427 | foreach ($cdn_dirs as &$dirvalue) { 428 | 429 | if (stripos($request,$dirvalue.'/') === 0){ 430 | 431 | $cdn_dirs_staus=1; //找到就将状态赋值为1,并且跳出本次循环。 432 | break; 433 | 434 | } 435 | 436 | } 437 | unset($dirvalue);//销毁目录的值 438 | 439 | }else{ 440 | //判断是否有预设的缓存目录,如果没有直接状态改为1; 441 | $cdn_dirs_staus = 1; 442 | } 443 | 444 | 445 | }else{ //判断是否有斜杠,没有斜杠意味着是一个根目录的文件。 446 | 447 | $cdn_dirs_staus = 1; 448 | 449 | } 450 | //如果状态为0,那么意味着不符合缓存目录的条件,缓存终止。 451 | if ($cdn_dirs_staus === 0){ 452 | exit(0); 453 | }else{ 454 | return 0; 455 | 456 | } 457 | 458 | 459 | /******目录检测结束 ********/ 460 | 461 | } 462 | 463 | 464 | } -------------------------------------------------------------------------------- /include/storages/bcs/libs/requestcore/requestcore.class.php: -------------------------------------------------------------------------------- 1 | ). 75 | */ 76 | public $request_class = 'BCS_RequestCore'; 77 | /** 78 | * The default class to use for HTTP Responses (defaults to ). 79 | */ 80 | public $response_class = 'BCS_ResponseCore'; 81 | /** 82 | * Default useragent string to use. 83 | */ 84 | public $useragent = 'BCS_RequestCore/1.4.2'; 85 | /** 86 | * File to read from while streaming up. 87 | */ 88 | public $read_file = null; 89 | /** 90 | * The resource to read from while streaming up. 91 | */ 92 | public $read_stream = null; 93 | /** 94 | * The size of the stream to read from. 95 | */ 96 | public $read_stream_size = null; 97 | /** 98 | * The length already read from the stream. 99 | */ 100 | public $read_stream_read = 0; 101 | /** 102 | * File to write to while streaming down. 103 | */ 104 | public $write_file = null; 105 | /** 106 | * The resource to write to while streaming down. 107 | */ 108 | public $write_stream = null; 109 | /** 110 | * Stores the intended starting seek position. 111 | */ 112 | public $seek_position = null; 113 | /** 114 | * The user-defined callback function to call when a stream is read from. 115 | */ 116 | public $registered_streaming_read_callback = null; 117 | /** 118 | * The user-defined callback function to call when a stream is written to. 119 | */ 120 | public $registered_streaming_write_callback = null; 121 | /*%******************************************************************************************%*/ 122 | // CONSTANTS 123 | /** 124 | * GET HTTP Method 125 | */ 126 | const HTTP_GET = 'GET'; 127 | /** 128 | * POST HTTP Method 129 | */ 130 | const HTTP_POST = 'POST'; 131 | /** 132 | * PUT HTTP Method 133 | */ 134 | const HTTP_PUT = 'PUT'; 135 | /** 136 | * DELETE HTTP Method 137 | */ 138 | const HTTP_DELETE = 'DELETE'; 139 | /** 140 | * HEAD HTTP Method 141 | */ 142 | const HTTP_HEAD = 'HEAD'; 143 | 144 | /*%******************************************************************************************%*/ 145 | // CONSTRUCTOR/DESTRUCTOR 146 | /** 147 | * Constructs a new instance of this class. 148 | * 149 | * @param string $url (Optional) The URL to request or service endpoint to query. 150 | * @param string $proxy (Optional) The faux-url to use for proxy settings. Takes the following format: `proxy://user:pass@hostname:port` 151 | * @param array $helpers (Optional) An associative array of classnames to use for request, and response functionality. Gets passed in automatically by the calling class. 152 | * @return $this A reference to the current instance. 153 | */ 154 | public function __construct($url = null, $proxy = null, $helpers = null) { 155 | // Set some default values. 156 | $this->request_url = $url; 157 | $this->method = self::HTTP_GET; 158 | $this->request_headers = array (); 159 | $this->request_body = ''; 160 | // Set a new Request class if one was set. 161 | if (isset ( $helpers ['request'] ) && ! empty ( $helpers ['request'] )) { 162 | $this->request_class = $helpers ['request']; 163 | } 164 | // Set a new Request class if one was set. 165 | if (isset ( $helpers ['response'] ) && ! empty ( $helpers ['response'] )) { 166 | $this->response_class = $helpers ['response']; 167 | } 168 | if ($proxy) { 169 | $this->set_proxy ( $proxy ); 170 | } 171 | return $this; 172 | } 173 | 174 | /** 175 | * Destructs the instance. Closes opened file handles. 176 | * 177 | * @return $this A reference to the current instance. 178 | */ 179 | public function __destruct() { 180 | if (isset ( $this->read_file ) && isset ( $this->read_stream )) { 181 | fclose ( $this->read_stream ); 182 | } 183 | if (isset ( $this->write_file ) && isset ( $this->write_stream )) { 184 | fclose ( $this->write_stream ); 185 | } 186 | return $this; 187 | } 188 | 189 | /*%******************************************************************************************%*/ 190 | // REQUEST METHODS 191 | /** 192 | * Sets the credentials to use for authentication. 193 | * 194 | * @param string $user (Required) The username to authenticate with. 195 | * @param string $pass (Required) The password to authenticate with. 196 | * @return $this A reference to the current instance. 197 | */ 198 | public function set_credentials($user, $pass) { 199 | $this->username = $user; 200 | $this->password = $pass; 201 | return $this; 202 | } 203 | 204 | /** 205 | * Adds a custom HTTP header to the cURL request. 206 | * 207 | * @param string $key (Required) The custom HTTP header to set. 208 | * @param mixed $value (Required) The value to assign to the custom HTTP header. 209 | * @return $this A reference to the current instance. 210 | */ 211 | public function add_header($key, $value) { 212 | $this->request_headers [$key] = $value; 213 | return $this; 214 | } 215 | 216 | /** 217 | * Removes an HTTP header from the cURL request. 218 | * 219 | * @param string $key (Required) The custom HTTP header to set. 220 | * @return $this A reference to the current instance. 221 | */ 222 | public function remove_header($key) { 223 | if (isset ( $this->request_headers [$key] )) { 224 | unset ( $this->request_headers [$key] ); 225 | } 226 | return $this; 227 | } 228 | 229 | /** 230 | * Set the method type for the request. 231 | * 232 | * @param string $method (Required) One of the following constants: , , , , . 233 | * @return $this A reference to the current instance. 234 | */ 235 | public function set_method($method) { 236 | $this->method = strtoupper ( $method ); 237 | return $this; 238 | } 239 | 240 | /** 241 | * Sets a custom useragent string for the class. 242 | * 243 | * @param string $ua (Required) The useragent string to use. 244 | * @return $this A reference to the current instance. 245 | */ 246 | public function set_useragent($ua) { 247 | $this->useragent = $ua; 248 | return $this; 249 | } 250 | 251 | /** 252 | * Set the body to send in the request. 253 | * 254 | * @param string $body (Required) The textual content to send along in the body of the request. 255 | * @return $this A reference to the current instance. 256 | */ 257 | public function set_body($body) { 258 | $this->request_body = $body; 259 | return $this; 260 | } 261 | 262 | /** 263 | * Set the URL to make the request to. 264 | * 265 | * @param string $url (Required) The URL to make the request to. 266 | * @return $this A reference to the current instance. 267 | */ 268 | public function set_request_url($url) { 269 | $this->request_url = $url; 270 | return $this; 271 | } 272 | 273 | /** 274 | * Set additional CURLOPT settings. These will merge with the default settings, and override if 275 | * there is a duplicate. 276 | * 277 | * @param array $curlopts (Optional) A set of key-value pairs that set `CURLOPT` options. These will merge with the existing CURLOPTs, and ones passed here will override the defaults. Keys should be the `CURLOPT_*` constants, not strings. 278 | * @return $this A reference to the current instance. 279 | */ 280 | public function set_curlopts($curlopts) { 281 | $this->curlopts = $curlopts; 282 | return $this; 283 | } 284 | 285 | /** 286 | * Sets the length in bytes to read from the stream while streaming up. 287 | * 288 | * @param integer $size (Required) The length in bytes to read from the stream. 289 | * @return $this A reference to the current instance. 290 | */ 291 | public function set_read_stream_size($size) { 292 | $this->read_stream_size = $size; 293 | return $this; 294 | } 295 | 296 | /** 297 | * Sets the resource to read from while streaming up. Reads the stream from its current position until 298 | * EOF or `$size` bytes have been read. If `$size` is not given it will be determined by and 299 | * . 300 | * 301 | * @param resource $resource (Required) The readable resource to read from. 302 | * @param integer $size (Optional) The size of the stream to read. 303 | * @return $this A reference to the current instance. 304 | */ 305 | public function set_read_stream($resource, $size = null) { 306 | if (! isset ( $size ) || $size < 0) { 307 | $stats = fstat ( $resource ); 308 | if ($stats && $stats ['size'] >= 0) { 309 | $position = ftell ( $resource ); 310 | if ($position !== false && $position >= 0) { 311 | $size = $stats ['size'] - $position; 312 | } 313 | } 314 | } 315 | $this->read_stream = $resource; 316 | return $this->set_read_stream_size ( $size ); 317 | } 318 | 319 | /** 320 | * Sets the file to read from while streaming up. 321 | * 322 | * @param string $location (Required) The readable location to read from. 323 | * @return $this A reference to the current instance. 324 | */ 325 | public function set_read_file($location) { 326 | $this->read_file = $location; 327 | $read_file_handle = fopen ( $location, 'r' ); 328 | return $this->set_read_stream ( $read_file_handle ); 329 | } 330 | 331 | /** 332 | * Sets the resource to write to while streaming down. 333 | * 334 | * @param resource $resource (Required) The writeable resource to write to. 335 | * @return $this A reference to the current instance. 336 | */ 337 | public function set_write_stream($resource) { 338 | $this->write_stream = $resource; 339 | return $this; 340 | } 341 | 342 | /** 343 | * Sets the file to write to while streaming down. 344 | * 345 | * @param string $location (Required) The writeable location to write to. 346 | * @return $this A reference to the current instance. 347 | */ 348 | public function set_write_file($location) { 349 | $this->write_file = $location; 350 | $write_file_handle = fopen ( $location, 'w' ); 351 | return $this->set_write_stream ( $write_file_handle ); 352 | } 353 | 354 | /** 355 | * Set the proxy to use for making requests. 356 | * 357 | * @param string $proxy (Required) The faux-url to use for proxy settings. Takes the following format: `proxy://user:pass@hostname:port` 358 | * @return $this A reference to the current instance. 359 | */ 360 | public function set_proxy($proxy) { 361 | $proxy = parse_url ( $proxy ); 362 | $proxy ['user'] = isset ( $proxy ['user'] ) ? $proxy ['user'] : null; 363 | $proxy ['pass'] = isset ( $proxy ['pass'] ) ? $proxy ['pass'] : null; 364 | $proxy ['port'] = isset ( $proxy ['port'] ) ? $proxy ['port'] : null; 365 | $this->proxy = $proxy; 366 | return $this; 367 | } 368 | 369 | /** 370 | * Set the intended starting seek position. 371 | * 372 | * @param integer $position (Required) The byte-position of the stream to begin reading from. 373 | * @return $this A reference to the current instance. 374 | */ 375 | public function set_seek_position($position) { 376 | $this->seek_position = isset ( $position ) ? ( integer ) $position : null; 377 | return $this; 378 | } 379 | 380 | /** 381 | * Register a callback function to execute whenever a data stream is read from using 382 | * . 383 | * 384 | * The user-defined callback function should accept three arguments: 385 | * 386 | *
    387 | *
  • $curl_handle - resource - Required - The cURL handle resource that represents the in-progress transfer.
  • 388 | *
  • $file_handle - resource - Required - The file handle resource that represents the file on the local file system.
  • 389 | *
  • $length - integer - Required - The length in kilobytes of the data chunk that was transferred.
  • 390 | *
391 | * 392 | * @param string|array|function $callback (Required) The callback function is called by , so you can pass the following values:
    393 | *
  • The name of a global function to execute, passed as a string.
  • 394 | *
  • A method to execute, passed as array('ClassName', 'MethodName').
  • 395 | *
  • An anonymous function (PHP 5.3+).
396 | * @return $this A reference to the current instance. 397 | */ 398 | public function register_streaming_read_callback($callback) { 399 | $this->registered_streaming_read_callback = $callback; 400 | return $this; 401 | } 402 | 403 | /** 404 | * Register a callback function to execute whenever a data stream is written to using 405 | * . 406 | * 407 | * The user-defined callback function should accept two arguments: 408 | * 409 | *
    410 | *
  • $curl_handle - resource - Required - The cURL handle resource that represents the in-progress transfer.
  • 411 | *
  • $length - integer - Required - The length in kilobytes of the data chunk that was transferred.
  • 412 | *
413 | * 414 | * @param string|array|function $callback (Required) The callback function is called by , so you can pass the following values:
    415 | *
  • The name of a global function to execute, passed as a string.
  • 416 | *
  • A method to execute, passed as array('ClassName', 'MethodName').
  • 417 | *
  • An anonymous function (PHP 5.3+).
418 | * @return $this A reference to the current instance. 419 | */ 420 | public function register_streaming_write_callback($callback) { 421 | $this->registered_streaming_write_callback = $callback; 422 | return $this; 423 | } 424 | 425 | /*%******************************************************************************************%*/ 426 | // PREPARE, SEND, AND PROCESS REQUEST 427 | /** 428 | * A callback function that is invoked by cURL for streaming up. 429 | * 430 | * @param resource $curl_handle (Required) The cURL handle for the request. 431 | * @param resource $file_handle (Required) The open file handle resource. 432 | * @param integer $length (Required) The maximum number of bytes to read. 433 | * @return binary Binary data from a stream. 434 | */ 435 | public function streaming_read_callback($curl_handle, $file_handle, $length) { 436 | // Once we've sent as much as we're supposed to send... 437 | if ($this->read_stream_read >= $this->read_stream_size) { 438 | // Send EOF 439 | return ''; 440 | } 441 | // If we're at the beginning of an upload and need to seek... 442 | if ($this->read_stream_read == 0 && isset ( $this->seek_position ) && $this->seek_position !== ftell ( $this->read_stream )) { 443 | if (fseek ( $this->read_stream, $this->seek_position ) !== 0) { 444 | throw new BCS_RequestCore_Exception ( 'The stream does not support seeking and is either not at the requested position or the position is unknown.' ); 445 | } 446 | } 447 | $read = fread ( $this->read_stream, min ( $this->read_stream_size - $this->read_stream_read, $length ) ); // Remaining upload data or cURL's requested chunk size 448 | $this->read_stream_read += strlen ( $read ); 449 | $out = $read === false ? '' : $read; 450 | // Execute callback function 451 | if ($this->registered_streaming_read_callback) { 452 | call_user_func ( $this->registered_streaming_read_callback, $curl_handle, $file_handle, $out ); 453 | } 454 | return $out; 455 | } 456 | 457 | /** 458 | * A callback function that is invoked by cURL for streaming down. 459 | * 460 | * @param resource $curl_handle (Required) The cURL handle for the request. 461 | * @param binary $data (Required) The data to write. 462 | * @return integer The number of bytes written. 463 | */ 464 | public function streaming_write_callback($curl_handle, $data) { 465 | $length = strlen ( $data ); 466 | $written_total = 0; 467 | $written_last = 0; 468 | while ( $written_total < $length ) { 469 | $written_last = fwrite ( $this->write_stream, substr ( $data, $written_total ) ); 470 | if ($written_last === false) { 471 | return $written_total; 472 | } 473 | $written_total += $written_last; 474 | } 475 | // Execute callback function 476 | if ($this->registered_streaming_write_callback) { 477 | call_user_func ( $this->registered_streaming_write_callback, $curl_handle, $written_total ); 478 | } 479 | return $written_total; 480 | } 481 | 482 | /** 483 | * Prepares and adds the details of the cURL request. This can be passed along to a 484 | * function. 485 | * 486 | * @return resource The handle for the cURL object. 487 | */ 488 | public function prep_request() { 489 | $curl_handle = curl_init (); 490 | // Set default options. 491 | curl_setopt ( $curl_handle, CURLOPT_URL, $this->request_url ); 492 | curl_setopt ( $curl_handle, CURLOPT_FILETIME, true ); 493 | curl_setopt ( $curl_handle, CURLOPT_FRESH_CONNECT, false ); 494 | curl_setopt ( $curl_handle, CURLOPT_SSL_VERIFYPEER, false ); 495 | curl_setopt ( $curl_handle, CURLOPT_SSL_VERIFYHOST, true ); 496 | curl_setopt ( $curl_handle, CURLOPT_CLOSEPOLICY, CURLCLOSEPOLICY_LEAST_RECENTLY_USED ); 497 | curl_setopt ( $curl_handle, CURLOPT_MAXREDIRS, 5 ); 498 | curl_setopt ( $curl_handle, CURLOPT_HEADER, true ); 499 | curl_setopt ( $curl_handle, CURLOPT_RETURNTRANSFER, true ); 500 | curl_setopt ( $curl_handle, CURLOPT_TIMEOUT, 5184000 ); 501 | curl_setopt ( $curl_handle, CURLOPT_CONNECTTIMEOUT, 120 ); 502 | curl_setopt ( $curl_handle, CURLOPT_NOSIGNAL, true ); 503 | curl_setopt ( $curl_handle, CURLOPT_REFERER, $this->request_url ); 504 | curl_setopt ( $curl_handle, CURLOPT_USERAGENT, $this->useragent ); 505 | curl_setopt ( $curl_handle, CURLOPT_READFUNCTION, array ( 506 | $this, 507 | 'streaming_read_callback' ) ); 508 | if ($this->debug_mode) { 509 | curl_setopt ( $curl_handle, CURLOPT_VERBOSE, true ); 510 | } 511 | //if (! ini_get ( 'safe_mode' )) { 512 | //modify by zhengkan 513 | //curl_setopt($curl_handle, CURLOPT_FOLLOWLOCATION, true); 514 | //} 515 | // Enable a proxy connection if requested. 516 | if ($this->proxy) { 517 | curl_setopt ( $curl_handle, CURLOPT_HTTPPROXYTUNNEL, true ); 518 | $host = $this->proxy ['host']; 519 | $host .= ($this->proxy ['port']) ? ':' . $this->proxy ['port'] : ''; 520 | curl_setopt ( $curl_handle, CURLOPT_PROXY, $host ); 521 | if (isset ( $this->proxy ['user'] ) && isset ( $this->proxy ['pass'] )) { 522 | curl_setopt ( $curl_handle, CURLOPT_PROXYUSERPWD, $this->proxy ['user'] . ':' . $this->proxy ['pass'] ); 523 | } 524 | } 525 | // Set credentials for HTTP Basic/Digest Authentication. 526 | if ($this->username && $this->password) { 527 | curl_setopt ( $curl_handle, CURLOPT_HTTPAUTH, CURLAUTH_ANY ); 528 | curl_setopt ( $curl_handle, CURLOPT_USERPWD, $this->username . ':' . $this->password ); 529 | } 530 | // Handle the encoding if we can. 531 | if (extension_loaded ( 'zlib' )) { 532 | curl_setopt ( $curl_handle, CURLOPT_ENCODING, '' ); 533 | } 534 | // Process custom headers 535 | if (isset ( $this->request_headers ) && count ( $this->request_headers )) { 536 | $temp_headers = array (); 537 | foreach ( $this->request_headers as $k => $v ) { 538 | $temp_headers [] = $k . ': ' . $v; 539 | } 540 | curl_setopt ( $curl_handle, CURLOPT_HTTPHEADER, $temp_headers ); 541 | } 542 | switch ($this->method) { 543 | case self::HTTP_PUT : 544 | curl_setopt ( $curl_handle, CURLOPT_CUSTOMREQUEST, 'PUT' ); 545 | if (isset ( $this->read_stream )) { 546 | if (! isset ( $this->read_stream_size ) || $this->read_stream_size < 0) { 547 | throw new BCS_RequestCore_Exception ( 'The stream size for the streaming upload cannot be determined.' ); 548 | } 549 | curl_setopt ( $curl_handle, CURLOPT_INFILESIZE, $this->read_stream_size ); 550 | curl_setopt ( $curl_handle, CURLOPT_UPLOAD, true ); 551 | } else { 552 | curl_setopt ( $curl_handle, CURLOPT_POSTFIELDS, $this->request_body ); 553 | } 554 | break; 555 | case self::HTTP_POST : 556 | curl_setopt ( $curl_handle, CURLOPT_POST, true ); 557 | curl_setopt ( $curl_handle, CURLOPT_POSTFIELDS, $this->request_body ); 558 | break; 559 | case self::HTTP_HEAD : 560 | curl_setopt ( $curl_handle, CURLOPT_CUSTOMREQUEST, self::HTTP_HEAD ); 561 | curl_setopt ( $curl_handle, CURLOPT_NOBODY, 1 ); 562 | break; 563 | default : // Assumed GET 564 | curl_setopt ( $curl_handle, CURLOPT_CUSTOMREQUEST, $this->method ); 565 | if (isset ( $this->write_stream )) { 566 | curl_setopt ( $curl_handle, CURLOPT_WRITEFUNCTION, array ( 567 | $this, 568 | 'streaming_write_callback' ) ); 569 | curl_setopt ( $curl_handle, CURLOPT_HEADER, false ); 570 | } else { 571 | curl_setopt ( $curl_handle, CURLOPT_POSTFIELDS, $this->request_body ); 572 | } 573 | break; 574 | } 575 | // Merge in the CURLOPTs 576 | if (isset ( $this->curlopts ) && sizeof ( $this->curlopts ) > 0) { 577 | foreach ( $this->curlopts as $k => $v ) { 578 | curl_setopt ( $curl_handle, $k, $v ); 579 | } 580 | } 581 | return $curl_handle; 582 | } 583 | 584 | /** 585 | * is the environment BAE? 586 | * @return boolean the result of the answer 587 | */ 588 | private function isBaeEnv() { 589 | if (isset ( $_SERVER ['HTTP_HOST'] )) { 590 | $host = $_SERVER ['HTTP_HOST']; 591 | $pos = strpos ( $host, '.' ); 592 | if ($pos !== false) { 593 | $substr = substr ( $host, $pos + 1 ); 594 | if ($substr == 'duapp.com') { 595 | return true; 596 | } 597 | } 598 | } 599 | if (isset ( $_SERVER ["HTTP_BAE_LOGID"] )) { 600 | return true; 601 | } 602 | 603 | return false; 604 | } 605 | 606 | /** 607 | * Take the post-processed cURL data and break it down into useful header/body/info chunks. Uses the 608 | * data stored in the `curl_handle` and `response` properties unless replacement data is passed in via 609 | * parameters. 610 | * 611 | * @param resource $curl_handle (Optional) The reference to the already executed cURL request. 612 | * @param string $response (Optional) The actual response content itself that needs to be parsed. 613 | * @return BCS_ResponseCore A object containing a parsed HTTP response. 614 | */ 615 | public function process_response($curl_handle = null, $response = null) { 616 | // Accept a custom one if it's passed. 617 | if ($curl_handle && $response) { 618 | $this->curl_handle = $curl_handle; 619 | $this->response = $response; 620 | } 621 | // As long as this came back as a valid resource... 622 | if (is_resource ( $this->curl_handle )) { 623 | // Determine what's what. 624 | $header_size = curl_getinfo ( $this->curl_handle, CURLINFO_HEADER_SIZE ); 625 | $this->response_headers = substr ( $this->response, 0, $header_size ); 626 | $this->response_body = substr ( $this->response, $header_size ); 627 | $this->response_code = curl_getinfo ( $this->curl_handle, CURLINFO_HTTP_CODE ); 628 | $this->response_info = curl_getinfo ( $this->curl_handle ); 629 | // Parse out the headers 630 | $this->response_headers = explode ( "\r\n\r\n", trim ( $this->response_headers ) ); 631 | $this->response_headers = array_pop ( $this->response_headers ); 632 | $this->response_headers = explode ( "\r\n", $this->response_headers ); 633 | array_shift ( $this->response_headers ); 634 | // Loop through and split up the headers. 635 | $header_assoc = array (); 636 | foreach ( $this->response_headers as $header ) { 637 | $kv = explode ( ': ', $header ); 638 | //$header_assoc [strtolower ( $kv [0] )] = $kv [1]; 639 | $header_assoc [$kv [0]] = $kv [1]; 640 | } 641 | // Reset the headers to the appropriate property. 642 | $this->response_headers = $header_assoc; 643 | $this->response_headers ['_info'] = $this->response_info; 644 | $this->response_headers ['_info'] ['method'] = $this->method; 645 | if ($curl_handle && $response) { 646 | return new $this->response_class ( $this->response_headers, $this->response_body, $this->response_code, $this->curl_handle ); 647 | } 648 | } 649 | // Return false 650 | return false; 651 | } 652 | 653 | /** 654 | * Sends the request, calling necessary utility functions to update built-in properties. 655 | * 656 | * @param boolean $parse (Optional) Whether to parse the response with BCS_ResponseCore or not. 657 | * @return string The resulting unparsed data from the request. 658 | */ 659 | public function send_request($parse = false) { 660 | if (false === $this->isBaeEnv ()) { 661 | set_time_limit ( 0 ); 662 | } 663 | $curl_handle = $this->prep_request (); 664 | $this->response = curl_exec ( $curl_handle ); 665 | if ($this->response === false) { 666 | throw new BCS_RequestCore_Exception ( 'cURL resource: ' . ( string ) $curl_handle . '; cURL error: ' . curl_error ( $curl_handle ) . ' (' . curl_errno ( $curl_handle ) . ')' ); 667 | } 668 | $parsed_response = $this->process_response ( $curl_handle, $this->response ); 669 | curl_close ( $curl_handle ); 670 | if ($parse) { 671 | return $parsed_response; 672 | } 673 | return $this->response; 674 | } 675 | 676 | /** 677 | * Sends the request using , enabling parallel requests. Uses the "rolling" method. 678 | * 679 | * @param array $handles (Required) An indexed array of cURL handles to process simultaneously. 680 | * @param array $opt (Optional) An associative array of parameters that can have the following keys:
    681 | *
  • callback - string|array - Optional - The string name of a function to pass the response data to. If this is a method, pass an array where the [0] index is the class and the [1] index is the method name.
  • 682 | *
  • limit - integer - Optional - The number of simultaneous requests to make. This can be useful for scaling around slow server responses. Defaults to trusting cURLs judgement as to how many to use.
683 | * @return array Post-processed cURL responses. 684 | */ 685 | public function send_multi_request($handles, $opt = null) { 686 | if (false === $this->isBaeEnv ()) { 687 | set_time_limit ( 0 ); 688 | } 689 | // Skip everything if there are no handles to process. 690 | if (count ( $handles ) === 0) 691 | return array (); 692 | if (! $opt) 693 | $opt = array (); 694 | 695 | // Initialize any missing options 696 | $limit = isset ( $opt ['limit'] ) ? $opt ['limit'] : - 1; 697 | // Initialize 698 | $handle_list = $handles; 699 | $http = new $this->request_class (); 700 | $multi_handle = curl_multi_init (); 701 | $handles_post = array (); 702 | $added = count ( $handles ); 703 | $last_handle = null; 704 | $count = 0; 705 | $i = 0; 706 | // Loop through the cURL handles and add as many as it set by the limit parameter. 707 | while ( $i < $added ) { 708 | if ($limit > 0 && $i >= $limit) 709 | break; 710 | curl_multi_add_handle ( $multi_handle, array_shift ( $handles ) ); 711 | $i ++; 712 | } 713 | do { 714 | $active = false; 715 | // Start executing and wait for a response. 716 | while ( ($status = curl_multi_exec ( $multi_handle, $active )) === CURLM_CALL_MULTI_PERFORM ) { 717 | // Start looking for possible responses immediately when we have to add more handles 718 | if (count ( $handles ) > 0) 719 | break; 720 | } 721 | // Figure out which requests finished. 722 | $to_process = array (); 723 | while ( $done = curl_multi_info_read ( $multi_handle ) ) { 724 | // Since curl_errno() isn't reliable for handles that were in multirequests, we check the 'result' of the info read, which contains the curl error number, (listed here http://curl.haxx.se/libcurl/c/libcurl-errors.html ) 725 | if ($done ['result'] > 0) { 726 | throw new BCS_RequestCore_Exception ( 'cURL resource: ' . ( string ) $done ['handle'] . '; cURL error: ' . curl_error ( $done ['handle'] ) . ' (' . $done ['result'] . ')' ); 727 | } // Because curl_multi_info_read() might return more than one message about a request, we check to see if this request is already in our array of completed requests 728 | elseif (! isset ( $to_process [( int ) $done ['handle']] )) { 729 | $to_process [( int ) $done ['handle']] = $done; 730 | } 731 | } 732 | // Actually deal with the request 733 | foreach ( $to_process as $pkey => $done ) { 734 | $response = $http->process_response ( $done ['handle'], curl_multi_getcontent ( $done ['handle'] ) ); 735 | $key = array_search ( $done ['handle'], $handle_list, true ); 736 | $handles_post [$key] = $response; 737 | if (count ( $handles ) > 0) { 738 | curl_multi_add_handle ( $multi_handle, array_shift ( $handles ) ); 739 | } 740 | curl_multi_remove_handle ( $multi_handle, $done ['handle'] ); 741 | curl_close ( $done ['handle'] ); 742 | } 743 | } while ( $active || count ( $handles_post ) < $added ); 744 | curl_multi_close ( $multi_handle ); 745 | ksort ( $handles_post, SORT_NUMERIC ); 746 | return $handles_post; 747 | } 748 | 749 | /*%******************************************************************************************%*/ 750 | // RESPONSE METHODS 751 | /** 752 | * Get the HTTP response headers from the request. 753 | * 754 | * @param string $header (Optional) A specific header value to return. Defaults to all headers. 755 | * @return string|array All or selected header values. 756 | */ 757 | public function get_response_header($header = null) { 758 | if ($header) { 759 | // return $this->response_headers [strtolower ( $header )]; 760 | return $this->response_headers [$header]; 761 | } 762 | return $this->response_headers; 763 | } 764 | 765 | /** 766 | * Get the HTTP response body from the request. 767 | * 768 | * @return string The response body. 769 | */ 770 | public function get_response_body() { 771 | return $this->response_body; 772 | } 773 | 774 | /** 775 | * Get the HTTP response code from the request. 776 | * 777 | * @return string The HTTP response code. 778 | */ 779 | public function get_response_code() { 780 | return $this->response_code; 781 | } 782 | } 783 | /** 784 | * Container for all response-related methods. 785 | */ 786 | class BCS_ResponseCore { 787 | /** 788 | * Stores the HTTP header information. 789 | */ 790 | public $header; 791 | /** 792 | * Stores the SimpleXML response. 793 | */ 794 | public $body; 795 | /** 796 | * Stores the HTTP response code. 797 | */ 798 | public $status; 799 | 800 | /** 801 | * Constructs a new instance of this class. 802 | * 803 | * @param array $header (Required) Associative array of HTTP headers (typically returned by ). 804 | * @param string $body (Required) XML-formatted response from AWS. 805 | * @param integer $status (Optional) HTTP response status code from the request. 806 | * @return object Contains an `header` property (HTTP headers as an associative array), a or `body` property, and an `status` code. 807 | */ 808 | public function __construct($header, $body, $status = null) { 809 | $this->header = $header; 810 | $this->body = $body; 811 | $this->status = $status; 812 | return $this; 813 | } 814 | 815 | /** 816 | * Did we receive the status code we expected? 817 | * 818 | * @param integer|array $codes (Optional) The status code(s) to expect. Pass an for a single acceptable value, or an of integers for multiple acceptable values. 819 | * @return boolean Whether we received the expected status code or not. 820 | */ 821 | public function isOK($codes = array(200, 201, 204, 206)) { 822 | if (is_array ( $codes )) { 823 | return in_array ( $this->status, $codes ); 824 | } 825 | return $this->status === $codes; 826 | } 827 | } 828 | /** 829 | * Default BCS_RequestCore Exception. 830 | */ 831 | class BCS_RequestCore_Exception extends Exception { 832 | } 833 | -------------------------------------------------------------------------------- /include/storages/oss/lib/requestcore/requestcore.class.php: -------------------------------------------------------------------------------- 1 | ). 91 | */ 92 | public $request_class = 'RequestCore'; 93 | 94 | /** 95 | * The default class to use for HTTP Responses (defaults to ). 96 | */ 97 | public $response_class = 'ResponseCore'; 98 | 99 | /** 100 | * Default useragent string to use. 101 | */ 102 | public $useragent = 'RequestCore/1.4.3'; 103 | 104 | /** 105 | * File to read from while streaming up. 106 | */ 107 | public $read_file = null; 108 | 109 | /** 110 | * The resource to read from while streaming up. 111 | */ 112 | public $read_stream = null; 113 | 114 | /** 115 | * The size of the stream to read from. 116 | */ 117 | public $read_stream_size = null; 118 | 119 | /** 120 | * The length already read from the stream. 121 | */ 122 | public $read_stream_read = 0; 123 | 124 | /** 125 | * File to write to while streaming down. 126 | */ 127 | public $write_file = null; 128 | 129 | /** 130 | * The resource to write to while streaming down. 131 | */ 132 | public $write_stream = null; 133 | 134 | /** 135 | * Stores the intended starting seek position. 136 | */ 137 | public $seek_position = null; 138 | 139 | /** 140 | * The location of the cacert.pem file to use. 141 | */ 142 | public $cacert_location = false; 143 | 144 | /** 145 | * The state of SSL certificate verification. 146 | */ 147 | public $ssl_verification = true; 148 | 149 | /** 150 | * The user-defined callback function to call when a stream is read from. 151 | */ 152 | public $registered_streaming_read_callback = null; 153 | 154 | /** 155 | * The user-defined callback function to call when a stream is written to. 156 | */ 157 | public $registered_streaming_write_callback = null; 158 | 159 | 160 | /*%******************************************************************************************%*/ 161 | // CONSTANTS 162 | 163 | /** 164 | * GET HTTP Method 165 | */ 166 | const HTTP_GET = 'GET'; 167 | 168 | /** 169 | * POST HTTP Method 170 | */ 171 | const HTTP_POST = 'POST'; 172 | 173 | /** 174 | * PUT HTTP Method 175 | */ 176 | const HTTP_PUT = 'PUT'; 177 | 178 | /** 179 | * DELETE HTTP Method 180 | */ 181 | const HTTP_DELETE = 'DELETE'; 182 | 183 | /** 184 | * HEAD HTTP Method 185 | */ 186 | const HTTP_HEAD = 'HEAD'; 187 | 188 | 189 | /*%******************************************************************************************%*/ 190 | // CONSTRUCTOR/DESTRUCTOR 191 | 192 | /** 193 | * Constructs a new instance of this class. 194 | * 195 | * @param string $url (Optional) The URL to request or service endpoint to query. 196 | * @param string $proxy (Optional) The faux-url to use for proxy settings. Takes the following format: `proxy://user:pass@hostname:port` 197 | * @param array $helpers (Optional) An associative array of classnames to use for request, and response functionality. Gets passed in automatically by the calling class. 198 | * @return $this A reference to the current instance. 199 | */ 200 | public function __construct($url = null, $proxy = null, $helpers = null) 201 | { 202 | // Set some default values. 203 | $this->request_url = $url; 204 | $this->method = self::HTTP_GET; 205 | $this->request_headers = array(); 206 | $this->request_body = ''; 207 | 208 | // Set a new Request class if one was set. 209 | if (isset($helpers['request']) && !empty($helpers['request'])) 210 | { 211 | $this->request_class = $helpers['request']; 212 | } 213 | 214 | // Set a new Request class if one was set. 215 | if (isset($helpers['response']) && !empty($helpers['response'])) 216 | { 217 | $this->response_class = $helpers['response']; 218 | } 219 | 220 | if ($proxy) 221 | { 222 | $this->set_proxy($proxy); 223 | } 224 | 225 | return $this; 226 | } 227 | 228 | /** 229 | * Destructs the instance. Closes opened file handles. 230 | * 231 | * @return $this A reference to the current instance. 232 | */ 233 | public function __destruct() 234 | { 235 | if (isset($this->read_file) && isset($this->read_stream)) 236 | { 237 | fclose($this->read_stream); 238 | } 239 | 240 | if (isset($this->write_file) && isset($this->write_stream)) 241 | { 242 | fclose($this->write_stream); 243 | } 244 | 245 | return $this; 246 | } 247 | 248 | 249 | /*%******************************************************************************************%*/ 250 | // REQUEST METHODS 251 | 252 | /** 253 | * Sets the credentials to use for authentication. 254 | * 255 | * @param string $user (Required) The username to authenticate with. 256 | * @param string $pass (Required) The password to authenticate with. 257 | * @return $this A reference to the current instance. 258 | */ 259 | public function set_credentials($user, $pass) 260 | { 261 | $this->username = $user; 262 | $this->password = $pass; 263 | return $this; 264 | } 265 | 266 | /** 267 | * Adds a custom HTTP header to the cURL request. 268 | * 269 | * @param string $key (Required) The custom HTTP header to set. 270 | * @param mixed $value (Required) The value to assign to the custom HTTP header. 271 | * @return $this A reference to the current instance. 272 | */ 273 | public function add_header($key, $value) 274 | { 275 | $this->request_headers[$key] = $value; 276 | return $this; 277 | } 278 | 279 | /** 280 | * Removes an HTTP header from the cURL request. 281 | * 282 | * @param string $key (Required) The custom HTTP header to set. 283 | * @return $this A reference to the current instance. 284 | */ 285 | public function remove_header($key) 286 | { 287 | if (isset($this->request_headers[$key])) 288 | { 289 | unset($this->request_headers[$key]); 290 | } 291 | return $this; 292 | } 293 | 294 | /** 295 | * Set the method type for the request. 296 | * 297 | * @param string $method (Required) One of the following constants: , , , , . 298 | * @return $this A reference to the current instance. 299 | */ 300 | public function set_method($method) 301 | { 302 | $this->method = strtoupper($method); 303 | return $this; 304 | } 305 | 306 | /** 307 | * Sets a custom useragent string for the class. 308 | * 309 | * @param string $ua (Required) The useragent string to use. 310 | * @return $this A reference to the current instance. 311 | */ 312 | public function set_useragent($ua) 313 | { 314 | $this->useragent = $ua; 315 | return $this; 316 | } 317 | 318 | /** 319 | * Set the body to send in the request. 320 | * 321 | * @param string $body (Required) The textual content to send along in the body of the request. 322 | * @return $this A reference to the current instance. 323 | */ 324 | public function set_body($body) 325 | { 326 | $this->request_body = $body; 327 | return $this; 328 | } 329 | 330 | /** 331 | * Set the URL to make the request to. 332 | * 333 | * @param string $url (Required) The URL to make the request to. 334 | * @return $this A reference to the current instance. 335 | */ 336 | public function set_request_url($url) 337 | { 338 | $this->request_url = $url; 339 | return $this; 340 | } 341 | 342 | /** 343 | * Set additional CURLOPT settings. These will merge with the default settings, and override if 344 | * there is a duplicate. 345 | * 346 | * @param array $curlopts (Optional) A set of key-value pairs that set `CURLOPT` options. These will merge with the existing CURLOPTs, and ones passed here will override the defaults. Keys should be the `CURLOPT_*` constants, not strings. 347 | * @return $this A reference to the current instance. 348 | */ 349 | public function set_curlopts($curlopts) 350 | { 351 | $this->curlopts = $curlopts; 352 | return $this; 353 | } 354 | 355 | /** 356 | * Sets the length in bytes to read from the stream while streaming up. 357 | * 358 | * @param integer $size (Required) The length in bytes to read from the stream. 359 | * @return $this A reference to the current instance. 360 | */ 361 | public function set_read_stream_size($size) 362 | { 363 | $this->read_stream_size = $size; 364 | 365 | return $this; 366 | } 367 | 368 | /** 369 | * Sets the resource to read from while streaming up. Reads the stream from its current position until 370 | * EOF or `$size` bytes have been read. If `$size` is not given it will be determined by and 371 | * . 372 | * 373 | * @param resource $resource (Required) The readable resource to read from. 374 | * @param integer $size (Optional) The size of the stream to read. 375 | * @return $this A reference to the current instance. 376 | */ 377 | public function set_read_stream($resource, $size = null) 378 | { 379 | if (!isset($size) || $size < 0) 380 | { 381 | $stats = fstat($resource); 382 | 383 | if ($stats && $stats['size'] >= 0) 384 | { 385 | $position = ftell($resource); 386 | 387 | if ($position !== false && $position >= 0) 388 | { 389 | $size = $stats['size'] - $position; 390 | } 391 | } 392 | } 393 | 394 | $this->read_stream = $resource; 395 | 396 | return $this->set_read_stream_size($size); 397 | } 398 | 399 | /** 400 | * Sets the file to read from while streaming up. 401 | * 402 | * @param string $location (Required) The readable location to read from. 403 | * @return $this A reference to the current instance. 404 | */ 405 | public function set_read_file($location) 406 | { 407 | $this->read_file = $location; 408 | $read_file_handle = fopen($location, 'r'); 409 | 410 | return $this->set_read_stream($read_file_handle); 411 | } 412 | 413 | /** 414 | * Sets the resource to write to while streaming down. 415 | * 416 | * @param resource $resource (Required) The writeable resource to write to. 417 | * @return $this A reference to the current instance. 418 | */ 419 | public function set_write_stream($resource) 420 | { 421 | $this->write_stream = $resource; 422 | 423 | return $this; 424 | } 425 | 426 | /** 427 | * Sets the file to write to while streaming down. 428 | * 429 | * @param string $location (Required) The writeable location to write to. 430 | * @return $this A reference to the current instance. 431 | */ 432 | public function set_write_file($location) 433 | { 434 | $this->write_file = $location; 435 | $write_file_handle = fopen($location, 'w'); 436 | 437 | return $this->set_write_stream($write_file_handle); 438 | } 439 | 440 | /** 441 | * Set the proxy to use for making requests. 442 | * 443 | * @param string $proxy (Required) The faux-url to use for proxy settings. Takes the following format: `proxy://user:pass@hostname:port` 444 | * @return $this A reference to the current instance. 445 | */ 446 | public function set_proxy($proxy) 447 | { 448 | $proxy = parse_url($proxy); 449 | $proxy['user'] = isset($proxy['user']) ? $proxy['user'] : null; 450 | $proxy['pass'] = isset($proxy['pass']) ? $proxy['pass'] : null; 451 | $proxy['port'] = isset($proxy['port']) ? $proxy['port'] : null; 452 | $this->proxy = $proxy; 453 | return $this; 454 | } 455 | 456 | /** 457 | * Set the intended starting seek position. 458 | * 459 | * @param integer $position (Required) The byte-position of the stream to begin reading from. 460 | * @return $this A reference to the current instance. 461 | */ 462 | public function set_seek_position($position) 463 | { 464 | $this->seek_position = isset($position) ? (integer) $position : null; 465 | 466 | return $this; 467 | } 468 | 469 | /** 470 | * Register a callback function to execute whenever a data stream is read from using 471 | * . 472 | * 473 | * The user-defined callback function should accept three arguments: 474 | * 475 | *
    476 | *
  • $curl_handle - resource - Required - The cURL handle resource that represents the in-progress transfer.
  • 477 | *
  • $file_handle - resource - Required - The file handle resource that represents the file on the local file system.
  • 478 | *
  • $length - integer - Required - The length in kilobytes of the data chunk that was transferred.
  • 479 | *
480 | * 481 | * @param string|array|function $callback (Required) The callback function is called by , so you can pass the following values:
    482 | *
  • The name of a global function to execute, passed as a string.
  • 483 | *
  • A method to execute, passed as array('ClassName', 'MethodName').
  • 484 | *
  • An anonymous function (PHP 5.3+).
485 | * @return $this A reference to the current instance. 486 | */ 487 | public function register_streaming_read_callback($callback) 488 | { 489 | $this->registered_streaming_read_callback = $callback; 490 | 491 | return $this; 492 | } 493 | 494 | /** 495 | * Register a callback function to execute whenever a data stream is written to using 496 | * . 497 | * 498 | * The user-defined callback function should accept two arguments: 499 | * 500 | *
    501 | *
  • $curl_handle - resource - Required - The cURL handle resource that represents the in-progress transfer.
  • 502 | *
  • $length - integer - Required - The length in kilobytes of the data chunk that was transferred.
  • 503 | *
504 | * 505 | * @param string|array|function $callback (Required) The callback function is called by , so you can pass the following values:
    506 | *
  • The name of a global function to execute, passed as a string.
  • 507 | *
  • A method to execute, passed as array('ClassName', 'MethodName').
  • 508 | *
  • An anonymous function (PHP 5.3+).
509 | * @return $this A reference to the current instance. 510 | */ 511 | public function register_streaming_write_callback($callback) 512 | { 513 | $this->registered_streaming_write_callback = $callback; 514 | 515 | return $this; 516 | } 517 | 518 | 519 | /*%******************************************************************************************%*/ 520 | // PREPARE, SEND, AND PROCESS REQUEST 521 | 522 | /** 523 | * A callback function that is invoked by cURL for streaming up. 524 | * 525 | * @param resource $curl_handle (Required) The cURL handle for the request. 526 | * @param resource $file_handle (Required) The open file handle resource. 527 | * @param integer $length (Required) The maximum number of bytes to read. 528 | * @return binary Binary data from a stream. 529 | */ 530 | public function streaming_read_callback($curl_handle, $file_handle, $length) 531 | { 532 | // Once we've sent as much as we're supposed to send... 533 | if ($this->read_stream_read >= $this->read_stream_size) 534 | { 535 | // Send EOF 536 | return ''; 537 | } 538 | 539 | // If we're at the beginning of an upload and need to seek... 540 | if ($this->read_stream_read == 0 && isset($this->seek_position) && $this->seek_position !== ftell($this->read_stream)) 541 | { 542 | if (fseek($this->read_stream, $this->seek_position) !== 0) 543 | { 544 | throw new RequestCore_Exception('The stream does not support seeking and is either not at the requested position or the position is unknown.'); 545 | } 546 | } 547 | 548 | $read = fread($this->read_stream, min($this->read_stream_size - $this->read_stream_read, $length)); // Remaining upload data or cURL's requested chunk size 549 | $this->read_stream_read += strlen($read); 550 | 551 | $out = $read === false ? '' : $read; 552 | 553 | // Execute callback function 554 | if ($this->registered_streaming_read_callback) 555 | { 556 | call_user_func($this->registered_streaming_read_callback, $curl_handle, $file_handle, $out); 557 | } 558 | 559 | return $out; 560 | } 561 | 562 | /** 563 | * A callback function that is invoked by cURL for streaming down. 564 | * 565 | * @param resource $curl_handle (Required) The cURL handle for the request. 566 | * @param binary $data (Required) The data to write. 567 | * @return integer The number of bytes written. 568 | */ 569 | public function streaming_write_callback($curl_handle, $data) 570 | { 571 | $length = strlen($data); 572 | $written_total = 0; 573 | $written_last = 0; 574 | 575 | while ($written_total < $length) 576 | { 577 | $written_last = fwrite($this->write_stream, substr($data, $written_total)); 578 | 579 | if ($written_last === false) 580 | { 581 | return $written_total; 582 | } 583 | 584 | $written_total += $written_last; 585 | } 586 | 587 | // Execute callback function 588 | if ($this->registered_streaming_write_callback) 589 | { 590 | call_user_func($this->registered_streaming_write_callback, $curl_handle, $written_total); 591 | } 592 | 593 | return $written_total; 594 | } 595 | 596 | /** 597 | * Prepares and adds the details of the cURL request. This can be passed along to a 598 | * function. 599 | * 600 | * @return resource The handle for the cURL object. 601 | */ 602 | public function prep_request() 603 | { 604 | $curl_handle = curl_init(); 605 | 606 | // Set default options. 607 | curl_setopt($curl_handle, CURLOPT_URL, $this->request_url); 608 | curl_setopt($curl_handle, CURLOPT_FILETIME, true); 609 | curl_setopt($curl_handle, CURLOPT_FRESH_CONNECT, false); 610 | curl_setopt($curl_handle, CURLOPT_CLOSEPOLICY, CURLCLOSEPOLICY_LEAST_RECENTLY_USED); 611 | curl_setopt($curl_handle, CURLOPT_MAXREDIRS, 5); 612 | curl_setopt($curl_handle, CURLOPT_HEADER, true); 613 | curl_setopt($curl_handle, CURLOPT_RETURNTRANSFER, true); 614 | curl_setopt($curl_handle, CURLOPT_TIMEOUT, 5184000); 615 | curl_setopt($curl_handle, CURLOPT_CONNECTTIMEOUT, 120); 616 | curl_setopt($curl_handle, CURLOPT_NOSIGNAL, true); 617 | curl_setopt($curl_handle, CURLOPT_REFERER, $this->request_url); 618 | curl_setopt($curl_handle, CURLOPT_USERAGENT, $this->useragent); 619 | curl_setopt($curl_handle, CURLOPT_READFUNCTION, array($this, 'streaming_read_callback')); 620 | 621 | // Verification of the SSL cert 622 | if ($this->ssl_verification) 623 | { 624 | curl_setopt($curl_handle, CURLOPT_SSL_VERIFYPEER, true); 625 | curl_setopt($curl_handle, CURLOPT_SSL_VERIFYHOST, true); 626 | } 627 | else 628 | { 629 | curl_setopt($curl_handle, CURLOPT_SSL_VERIFYPEER, false); 630 | curl_setopt($curl_handle, CURLOPT_SSL_VERIFYHOST, false); 631 | } 632 | 633 | // chmod the file as 0755 634 | if ($this->cacert_location === true) 635 | { 636 | curl_setopt($curl_handle, CURLOPT_CAINFO, dirname(__FILE__) . '/cacert.pem'); 637 | } 638 | elseif (is_string($this->cacert_location)) 639 | { 640 | curl_setopt($curl_handle, CURLOPT_CAINFO, $this->cacert_location); 641 | } 642 | 643 | // Debug mode 644 | if ($this->debug_mode) 645 | { 646 | curl_setopt($curl_handle, CURLOPT_VERBOSE, true); 647 | } 648 | 649 | // Handle open_basedir & safe mode 650 | if (!ini_get('safe_mode') && !ini_get('open_basedir')) 651 | { 652 | curl_setopt($curl_handle, CURLOPT_FOLLOWLOCATION, true); 653 | } 654 | 655 | // Enable a proxy connection if requested. 656 | if ($this->proxy) 657 | { 658 | curl_setopt($curl_handle, CURLOPT_HTTPPROXYTUNNEL, true); 659 | 660 | $host = $this->proxy['host']; 661 | $host .= ($this->proxy['port']) ? ':' . $this->proxy['port'] : ''; 662 | curl_setopt($curl_handle, CURLOPT_PROXY, $host); 663 | 664 | if (isset($this->proxy['user']) && isset($this->proxy['pass'])) 665 | { 666 | curl_setopt($curl_handle, CURLOPT_PROXYUSERPWD, $this->proxy['user'] . ':' . $this->proxy['pass']); 667 | } 668 | } 669 | 670 | // Set credentials for HTTP Basic/Digest Authentication. 671 | if ($this->username && $this->password) 672 | { 673 | curl_setopt($curl_handle, CURLOPT_HTTPAUTH, CURLAUTH_ANY); 674 | curl_setopt($curl_handle, CURLOPT_USERPWD, $this->username . ':' . $this->password); 675 | } 676 | 677 | // Handle the encoding if we can. 678 | if (extension_loaded('zlib')) 679 | { 680 | curl_setopt($curl_handle, CURLOPT_ENCODING, ''); 681 | } 682 | 683 | // Process custom headers 684 | if (isset($this->request_headers) && count($this->request_headers)) 685 | { 686 | $temp_headers = array(); 687 | 688 | foreach ($this->request_headers as $k => $v) 689 | { 690 | $temp_headers[] = $k . ': ' . $v; 691 | } 692 | 693 | curl_setopt($curl_handle, CURLOPT_HTTPHEADER, $temp_headers); 694 | } 695 | 696 | switch ($this->method) 697 | { 698 | case self::HTTP_PUT: 699 | //unset($this->read_stream); 700 | curl_setopt($curl_handle, CURLOPT_CUSTOMREQUEST, 'PUT'); 701 | if (isset($this->read_stream)) 702 | { 703 | if (!isset($this->read_stream_size) || $this->read_stream_size < 0) 704 | { 705 | throw new RequestCore_Exception('The stream size for the streaming upload cannot be determined.'); 706 | } 707 | 708 | curl_setopt($curl_handle, CURLOPT_INFILESIZE, $this->read_stream_size); 709 | curl_setopt($curl_handle, CURLOPT_UPLOAD, true); 710 | } 711 | else 712 | { 713 | curl_setopt($curl_handle, CURLOPT_POSTFIELDS, $this->request_body); 714 | } 715 | break; 716 | 717 | case self::HTTP_POST: 718 | curl_setopt($curl_handle, CURLOPT_POST, true); 719 | curl_setopt($curl_handle, CURLOPT_POSTFIELDS, $this->request_body); 720 | break; 721 | 722 | case self::HTTP_HEAD: 723 | curl_setopt($curl_handle, CURLOPT_CUSTOMREQUEST, self::HTTP_HEAD); 724 | curl_setopt($curl_handle, CURLOPT_NOBODY, 1); 725 | break; 726 | 727 | default: // Assumed GET 728 | curl_setopt($curl_handle, CURLOPT_CUSTOMREQUEST, $this->method); 729 | if (isset($this->write_stream)) 730 | { 731 | curl_setopt($curl_handle, CURLOPT_WRITEFUNCTION, array($this, 'streaming_write_callback')); 732 | curl_setopt($curl_handle, CURLOPT_HEADER, false); 733 | } 734 | else 735 | { 736 | curl_setopt($curl_handle, CURLOPT_POSTFIELDS, $this->request_body); 737 | } 738 | break; 739 | } 740 | 741 | // Merge in the CURLOPTs 742 | if (isset($this->curlopts) && sizeof($this->curlopts) > 0) 743 | { 744 | foreach ($this->curlopts as $k => $v) 745 | { 746 | curl_setopt($curl_handle, $k, $v); 747 | } 748 | } 749 | 750 | return $curl_handle; 751 | } 752 | 753 | /** 754 | * Take the post-processed cURL data and break it down into useful header/body/info chunks. Uses the 755 | * data stored in the `curl_handle` and `response` properties unless replacement data is passed in via 756 | * parameters. 757 | * 758 | * @param resource $curl_handle (Optional) The reference to the already executed cURL request. 759 | * @param string $response (Optional) The actual response content itself that needs to be parsed. 760 | * @return ResponseCore A object containing a parsed HTTP response. 761 | */ 762 | public function process_response($curl_handle = null, $response = null) 763 | { 764 | // Accept a custom one if it's passed. 765 | if ($curl_handle && $response) 766 | { 767 | $this->curl_handle = $curl_handle; 768 | $this->response = $response; 769 | } 770 | 771 | // As long as this came back as a valid resource... 772 | if (is_resource($this->curl_handle)) 773 | { 774 | // Determine what's what. 775 | $header_size = curl_getinfo($this->curl_handle, CURLINFO_HEADER_SIZE); 776 | $this->response_headers = substr($this->response, 0, $header_size); 777 | $this->response_body = substr($this->response, $header_size); 778 | $this->response_code = curl_getinfo($this->curl_handle, CURLINFO_HTTP_CODE); 779 | $this->response_info = curl_getinfo($this->curl_handle); 780 | 781 | // Parse out the headers 782 | $this->response_headers = explode("\r\n\r\n", trim($this->response_headers)); 783 | $this->response_headers = array_pop($this->response_headers); 784 | $this->response_headers = explode("\r\n", $this->response_headers); 785 | array_shift($this->response_headers); 786 | 787 | // Loop through and split up the headers. 788 | $header_assoc = array(); 789 | foreach ($this->response_headers as $header) 790 | { 791 | $kv = explode(': ', $header); 792 | $header_assoc[strtolower($kv[0])] = isset($kv[1])?$kv[1]:''; 793 | } 794 | 795 | // Reset the headers to the appropriate property. 796 | $this->response_headers = $header_assoc; 797 | $this->response_headers['_info'] = $this->response_info; 798 | $this->response_headers['_info']['method'] = $this->method; 799 | 800 | if ($curl_handle && $response) 801 | { 802 | return new $this->response_class($this->response_headers, $this->response_body, $this->response_code, $this->curl_handle); 803 | } 804 | } 805 | 806 | // Return false 807 | return false; 808 | } 809 | 810 | /** 811 | * Sends the request, calling necessary utility functions to update built-in properties. 812 | * 813 | * @param boolean $parse (Optional) Whether to parse the response with ResponseCore or not. 814 | * @return string The resulting unparsed data from the request. 815 | */ 816 | public function send_request($parse = false) 817 | { 818 | set_time_limit(0); 819 | 820 | $curl_handle = $this->prep_request(); 821 | $this->response = curl_exec($curl_handle); 822 | 823 | if ($this->response === false) 824 | { 825 | throw new RequestCore_Exception('cURL resource: ' . (string) $curl_handle . '; cURL error: ' . curl_error($curl_handle) . ' (' . curl_errno($curl_handle) . ')'); 826 | } 827 | 828 | $parsed_response = $this->process_response($curl_handle, $this->response); 829 | 830 | curl_close($curl_handle); 831 | 832 | if ($parse) 833 | { 834 | return $parsed_response; 835 | } 836 | 837 | return $this->response; 838 | } 839 | 840 | /** 841 | * Sends the request using , enabling parallel requests. Uses the "rolling" method. 842 | * 843 | * @param array $handles (Required) An indexed array of cURL handles to process simultaneously. 844 | * @param array $opt (Optional) An associative array of parameters that can have the following keys:
    845 | *
  • callback - string|array - Optional - The string name of a function to pass the response data to. If this is a method, pass an array where the [0] index is the class and the [1] index is the method name.
  • 846 | *
  • limit - integer - Optional - The number of simultaneous requests to make. This can be useful for scaling around slow server responses. Defaults to trusting cURLs judgement as to how many to use.
847 | * @return array Post-processed cURL responses. 848 | */ 849 | public function send_multi_request($handles, $opt = null) 850 | { 851 | set_time_limit(0); 852 | 853 | // Skip everything if there are no handles to process. 854 | if (count($handles) === 0) return array(); 855 | 856 | if (!$opt) $opt = array(); 857 | 858 | // Initialize any missing options 859 | $limit = isset($opt['limit']) ? $opt['limit'] : -1; 860 | 861 | // Initialize 862 | $handle_list = $handles; 863 | $http = new $this->request_class(); 864 | $multi_handle = curl_multi_init(); 865 | $handles_post = array(); 866 | $added = count($handles); 867 | $last_handle = null; 868 | $count = 0; 869 | $i = 0; 870 | 871 | // Loop through the cURL handles and add as many as it set by the limit parameter. 872 | while ($i < $added) 873 | { 874 | if ($limit > 0 && $i >= $limit) break; 875 | curl_multi_add_handle($multi_handle, array_shift($handles)); 876 | $i++; 877 | } 878 | 879 | do 880 | { 881 | $active = false; 882 | 883 | // Start executing and wait for a response. 884 | while (($status = curl_multi_exec($multi_handle, $active)) === CURLM_CALL_MULTI_PERFORM) 885 | { 886 | // Start looking for possible responses immediately when we have to add more handles 887 | if (count($handles) > 0) break; 888 | } 889 | 890 | // Figure out which requests finished. 891 | $to_process = array(); 892 | 893 | while ($done = curl_multi_info_read($multi_handle)) 894 | { 895 | // Since curl_errno() isn't reliable for handles that were in multirequests, we check the 'result' of the info read, which contains the curl error number, (listed here http://curl.haxx.se/libcurl/c/libcurl-errors.html ) 896 | if ($done['result'] > 0) 897 | { 898 | throw new RequestCore_Exception('cURL resource: ' . (string) $done['handle'] . '; cURL error: ' . curl_error($done['handle']) . ' (' . $done['result'] . ')'); 899 | } 900 | 901 | // Because curl_multi_info_read() might return more than one message about a request, we check to see if this request is already in our array of completed requests 902 | elseif (!isset($to_process[(int) $done['handle']])) 903 | { 904 | $to_process[(int) $done['handle']] = $done; 905 | } 906 | } 907 | 908 | // Actually deal with the request 909 | foreach ($to_process as $pkey => $done) 910 | { 911 | $response = $http->process_response($done['handle'], curl_multi_getcontent($done['handle'])); 912 | $key = array_search($done['handle'], $handle_list, true); 913 | $handles_post[$key] = $response; 914 | 915 | if (count($handles) > 0) 916 | { 917 | curl_multi_add_handle($multi_handle, array_shift($handles)); 918 | } 919 | 920 | curl_multi_remove_handle($multi_handle, $done['handle']); 921 | curl_close($done['handle']); 922 | } 923 | } 924 | while ($active || count($handles_post) < $added); 925 | 926 | curl_multi_close($multi_handle); 927 | 928 | ksort($handles_post, SORT_NUMERIC); 929 | return $handles_post; 930 | } 931 | 932 | 933 | /*%******************************************************************************************%*/ 934 | // RESPONSE METHODS 935 | 936 | /** 937 | * Get the HTTP response headers from the request. 938 | * 939 | * @param string $header (Optional) A specific header value to return. Defaults to all headers. 940 | * @return string|array All or selected header values. 941 | */ 942 | public function get_response_header($header = null) 943 | { 944 | if ($header) 945 | { 946 | return $this->response_headers[strtolower($header)]; 947 | } 948 | return $this->response_headers; 949 | } 950 | 951 | /** 952 | * Get the HTTP response body from the request. 953 | * 954 | * @return string The response body. 955 | */ 956 | public function get_response_body() 957 | { 958 | return $this->response_body; 959 | } 960 | 961 | /** 962 | * Get the HTTP response code from the request. 963 | * 964 | * @return string The HTTP response code. 965 | */ 966 | public function get_response_code() 967 | { 968 | return $this->response_code; 969 | } 970 | } 971 | 972 | 973 | /** 974 | * Container for all response-related methods. 975 | */ 976 | class ResponseCore 977 | { 978 | /** 979 | * Stores the HTTP header information. 980 | */ 981 | public $header; 982 | 983 | /** 984 | * Stores the SimpleXML response. 985 | */ 986 | public $body; 987 | 988 | /** 989 | * Stores the HTTP response code. 990 | */ 991 | public $status; 992 | 993 | /** 994 | * Constructs a new instance of this class. 995 | * 996 | * @param array $header (Required) Associative array of HTTP headers (typically returned by ). 997 | * @param string $body (Required) XML-formatted response from AWS. 998 | * @param integer $status (Optional) HTTP response status code from the request. 999 | * @return object Contains an `header` property (HTTP headers as an associative array), a or `body` property, and an `status` code. 1000 | */ 1001 | public function __construct($header, $body, $status = null) 1002 | { 1003 | $this->header = $header; 1004 | $this->body = $body; 1005 | $this->status = $status; 1006 | 1007 | return $this; 1008 | } 1009 | 1010 | /** 1011 | * Did we receive the status code we expected? 1012 | * 1013 | * @param integer|array $codes (Optional) The status code(s) to expect. Pass an for a single acceptable value, or an of integers for multiple acceptable values. 1014 | * @return boolean Whether we received the expected status code or not. 1015 | */ 1016 | public function isOK($codes = array(200, 201, 204, 206)) 1017 | { 1018 | if (is_array($codes)) 1019 | { 1020 | return in_array($this->status, $codes); 1021 | } 1022 | 1023 | return $this->status === $codes; 1024 | } 1025 | } 1026 | 1027 | /** 1028 | * Default RequestCore Exception. 1029 | */ 1030 | class RequestCore_Exception extends Exception {} 1031 | -------------------------------------------------------------------------------- /include/storages/bcs/bcs.class.php: -------------------------------------------------------------------------------- 1 | ak = $ak; 115 | $this->sk = $sk; 116 | } elseif (defined ( 'BCS_AK' ) && defined ( 'BCS_SK' ) && strlen ( BCS_AK ) > 0 && strlen ( BCS_SK ) > 0) { 117 | $this->ak = BCS_AK; 118 | $this->sk = BCS_SK; 119 | } elseif (false !== getenv ( 'HTTP_BAE_ENV_AK' ) && false !== getenv ( 'HTTP_BAE_ENV_SK' )) { 120 | $this->ak = getenv ( 'HTTP_BAE_ENV_AK' ); 121 | $this->sk = getenv ( 'HTTP_BAE_ENV_SK' ); 122 | } else { 123 | throw new BCS_Exception ( 'Construct can not get ak &sk pair, please check!' ); 124 | } 125 | //valid $hostname 126 | if (NULL !== $hostname) { 127 | $this->hostname = $hostname; 128 | } elseif (false !== getenv ( 'HTTP_BAE_ENV_ADDR_BCS' )) { 129 | $this->hostname = getenv ( 'HTTP_BAE_ENV_ADDR_BCS' ); 130 | } else { 131 | $this->hostname = self::DEFAULT_URL; 132 | } 133 | } 134 | 135 | /** 136 | * 将消息发往Baidu BCS. 137 | * @param array $opt 138 | * @return BCS_ResponseCore 139 | */ 140 | private function authenticate($opt) { 141 | //set common param into opt 142 | $opt [self::AK] = $this->ak; 143 | $opt [self::SK] = $this->sk; 144 | 145 | // Validate the S3 bucket name, only list_bucket didnot need validate_bucket 146 | if (! ('/' == $opt [self::OBJECT] && '' == $opt [self::BUCKET] && 'GET' == $opt [self::METHOD] && ! isset ( $opt [self::QUERY_STRING] [self::ACL] )) && ! self::validate_bucket ( $opt [self::BUCKET] )) { 147 | throw new BCS_Exception ( $opt [self::BUCKET] . 'is not valid, please check!' ); 148 | } 149 | //Validate object 150 | if (isset ( $opt [self::OBJECT] ) && ! self::validate_object ( $opt [self::OBJECT] )) { 151 | throw new BCS_Exception ( "Invalid object param[" . $opt [self::OBJECT] . "], please check.", - 1 ); 152 | } 153 | //construct url 154 | $url = $this->format_url ( $opt ); 155 | if ($url === false) { 156 | throw new BCS_Exception ( 'Can not format url, please check your param!', - 1 ); 157 | } 158 | $opt ['url'] = $url; 159 | $this->log ( "[method:" . $opt [self::METHOD] . "][url:$url]", $opt ); 160 | //build request 161 | $request = new BCS_RequestCore ( $opt ['url'] ); 162 | $headers = array ( 163 | 'Content-Type' => 'application/x-www-form-urlencoded' ); 164 | 165 | $request->set_method ( $opt [self::METHOD] ); 166 | //Write get_object content to fileWriteTo 167 | if (isset ( $opt ['fileWriteTo'] )) { 168 | $request->set_write_file ( $opt ['fileWriteTo'] ); 169 | } 170 | // Merge the HTTP headers 171 | if (isset ( $opt [self::HEADERS] )) { 172 | $headers = array_merge ( $headers, $opt [self::HEADERS] ); 173 | } 174 | // Set content to Http-Body 175 | if (isset ( $opt ['content'] )) { 176 | $request->set_body ( $opt ['content'] ); 177 | } 178 | // Upload file 179 | if (isset ( $opt ['fileUpload'] )) { 180 | if (! file_exists ( $opt ['fileUpload'] )) { 181 | throw new BCS_Exception ( 'File[' . $opt ['fileUpload'] . '] not found!', - 1 ); 182 | } 183 | $request->set_read_file ( $opt ['fileUpload'] ); 184 | // Determine the length to read from the file 185 | $length = $request->read_stream_size; // The file size by default 186 | $file_size = $length; 187 | if (isset ( $opt ["length"] )) { 188 | if ($opt ["length"] > $file_size) { 189 | throw new BCS_Exception ( "Input opt[length] invalid! It can not bigger than file-size", - 1 ); 190 | } 191 | $length = $opt ['length']; 192 | } 193 | if (isset ( $opt ['seekTo'] ) && ! isset ( $opt ["length"] )) { 194 | // Read from seekTo until EOF by default, when set seekTo but not set $opt["length"] 195 | $length -= ( integer ) $opt ['seekTo']; 196 | } 197 | $request->set_read_stream_size ( $length ); 198 | // Attempt to guess the correct mime-type 199 | if ($headers ['Content-Type'] === 'application/x-www-form-urlencoded') { 200 | $extension = explode ( '.', $opt ['fileUpload'] ); 201 | $extension = array_pop ( $extension ); 202 | $mime_type = BCS_MimeTypes::get_mimetype ( $extension ); 203 | $headers ['Content-Type'] = $mime_type; 204 | } 205 | $headers ['Content-MD5'] = ''; 206 | } 207 | // Handle streaming file offsets 208 | if (isset ( $opt ['seekTo'] )) { 209 | // Pass the seek position to BCS_RequestCore 210 | $request->set_seek_position ( ( integer ) $opt ['seekTo'] ); 211 | } 212 | // Add headers to request and compute the string to sign 213 | foreach ( $headers as $header_key => $header_value ) { 214 | // Strip linebreaks from header values as they're illegal and can allow for security issues 215 | $header_value = str_replace ( array ( 216 | "\r", 217 | "\n" ), '', $header_value ); 218 | // Add the header if it has a value 219 | if ($header_value !== '') { 220 | $request->add_header ( $header_key, $header_value ); 221 | } 222 | } 223 | // Set the curl options. 224 | if (isset ( $opt ['curlopts'] ) && count ( $opt ['curlopts'] )) { 225 | $request->set_curlopts ( $opt ['curlopts'] ); 226 | } 227 | $request->send_request (); 228 | return new BCS_ResponseCore ( $request->get_response_header (), $request->get_response_body (), $request->get_response_code () ); 229 | } 230 | 231 | /** 232 | * 获取当前密钥对拥有者的bucket列表 233 | * @param array $opt (Optional) 234 | * BaiduBCS::IMPORT_BCS_LOG_METHOD - String - Optional: 支持用户传入日志处理函数,函数定义如 function f($log) 235 | * @throws BCS_Exception 236 | * @return BCS_ResponseCore 237 | */ 238 | public function list_bucket($opt = array()) { 239 | $this->assertParameterArray ( $opt ); 240 | $opt [self::BUCKET] = ''; 241 | $opt [self::METHOD] = 'GET'; 242 | $opt [self::OBJECT] = '/'; 243 | $response = $this->authenticate ( $opt ); 244 | $this->log ( $response->isOK () ? "List bucket success!" : "List bucket failed! Response: [" . $response->body . "]", $opt ); 245 | return $response; 246 | } 247 | 248 | /** 249 | * 创建 bucket 250 | * @param string $bucket (Required) bucket名称 251 | * @param string $acl (Optional) bucket权限设置,若为null,使用server分配的默认权限 252 | * @param array $opt (Optional) 253 | * @throws BCS_Exception 254 | * @return BCS_ResponseCore 255 | */ 256 | public function create_bucket($bucket, $acl = NULL, $opt = array()) { 257 | $this->assertParameterArray ( $opt ); 258 | $opt [self::BUCKET] = $bucket; 259 | $opt [self::METHOD] = 'PUT'; 260 | $opt [self::OBJECT] = '/'; 261 | if (NULL !== $acl) { 262 | if (! in_array ( $acl, self::$ACL_TYPES )) { 263 | throw new BCS_Exception ( "Invalid acl_type[" . $acl . "], please check!", - 1 ); 264 | } 265 | self::set_header_into_opt ( "x-bs-acl", $acl, $opt ); 266 | } 267 | $response = $this->authenticate ( $opt ); 268 | $this->log ( $response->isOK () ? "Create bucket success!" : "Create bucket failed! Response: [" . $response->body . "]", $opt ); 269 | return $response; 270 | } 271 | 272 | /** 273 | * 删除bucket 274 | * @param string $bucket (Required) 275 | * @param array $opt (Optional) 276 | * @return boolean|BCS_ResponseCore 277 | */ 278 | public function delete_bucket($bucket, $opt = array()) { 279 | $this->assertParameterArray ( $opt ); 280 | $opt [self::BUCKET] = $bucket; 281 | $opt [self::METHOD] = 'DELETE'; 282 | $opt [self::OBJECT] = '/'; 283 | $response = $this->authenticate ( $opt ); 284 | $this->log ( $response->isOK () ? "Delete bucket success!" : "Delete bucket failed! Response: [" . $response->body . "]", $opt ); 285 | return $response; 286 | } 287 | 288 | /** 289 | * 设置bucket的acl,有三种模式, 290 | * (1).设置详细json格式的acl; 291 | * a. $acl 为json的array 292 | * b. $acl 为json的string 293 | * (2).通过acl_type字段进行设置 294 | * a. $acl 为BaiduBCS::$ACL_TYPES中的字段 295 | * @param string $bucket (Required) 296 | * @param string $acl (Required) 297 | * @param array $opt (Optional) 298 | * @return boolean|BCS_ResponseCore 299 | */ 300 | public function set_bucket_acl($bucket, $acl, $opt = array()) { 301 | $this->assertParameterArray ( $opt ); 302 | $result = $this->analyze_user_acl ( $acl ); 303 | $opt = array_merge ( $opt, $result ); 304 | $opt [self::BUCKET] = $bucket; 305 | $opt [self::METHOD] = 'PUT'; 306 | $opt [self::OBJECT] = '/'; 307 | $opt [self::QUERY_STRING] = array ( 308 | self::ACL => 1 ); 309 | $response = $this->authenticate ( $opt ); 310 | $this->log ( $response->isOK () ? "Set bucket acl success!" : "Set bucket acl failed! Response: [" . $response->body . "]", $opt ); 311 | return $response; 312 | } 313 | 314 | /** 315 | * 获取bucket的acl 316 | * @param string $bucket (Required) 317 | * @param array $opt (Optional) 318 | * @return BCS_ResponseCore 319 | */ 320 | public function get_bucket_acl($bucket, $opt = array()) { 321 | $this->assertParameterArray ( $opt ); 322 | $opt [self::BUCKET] = $bucket; 323 | $opt [self::METHOD] = 'GET'; 324 | $opt [self::OBJECT] = '/'; 325 | $opt [self::QUERY_STRING] = array ( 326 | self::ACL => 1 ); 327 | $response = $this->authenticate ( $opt ); 328 | $this->log ( $response->isOK () ? "Get bucket acl success!" : "Get bucket acl failed! Response: [" . $response->body . "]", $opt ); 329 | return $response; 330 | } 331 | 332 | /** 333 | * 获取bucket中object列表 334 | * @param string $bucket (Required) 335 | * @param array $opt (Optional) 336 | * start : 主要用于翻页功能,用法同mysql中start的用法 337 | * limit : 主要用于翻页功能,用法同mysql中limit的用法 338 | * prefix: 只返回以prefix为前缀的object,此处prefix必须以'/'开头 339 | * @throws BCS_Exception 340 | * @return BCS_ResponseCore 341 | */ 342 | public function list_object($bucket, $opt = array()) { 343 | $this->assertParameterArray ( $opt ); 344 | $opt [self::BUCKET] = $bucket; 345 | if (empty ( $opt [self::BUCKET] )) { 346 | throw new BCS_Exception ( "Bucket should not be empty, please check", - 1 ); 347 | } 348 | $opt [self::METHOD] = 'GET'; 349 | $opt [self::OBJECT] = '/'; 350 | $opt [self::QUERY_STRING] = array (); 351 | if (isset ( $opt ['start'] ) && is_int ( $opt ['start'] )) { 352 | $opt [self::QUERY_STRING] ['start'] = $opt ['start']; 353 | } 354 | if (isset ( $opt ['limit'] ) && is_int ( $opt ['limit'] )) { 355 | $opt [self::QUERY_STRING] ['limit'] = $opt ['limit']; 356 | } 357 | if (isset ( $opt ['prefix'] )) { 358 | $opt [self::QUERY_STRING] ['prefix'] = rawurlencode ( $opt ['prefix'] ); 359 | } 360 | $response = $this->authenticate ( $opt ); 361 | $this->log ( $response->isOK () ? "List object success!" : "Lit object failed! Response: [" . $response->body . "]", $opt ); 362 | return $response; 363 | } 364 | 365 | /** 366 | * 以目录形式获取bucket中object列表 367 | * @param string $bucket (Required) 368 | * @param $dir (Required) 369 | * 目录名,格式为必须以'/'开头和结尾,默认为'/' 370 | * @param string $list_model (Required) 371 | * 目录展现形式,值可以为0,1,2,默认为2,以下对各个值的功能进行介绍: 372 | * 0->只返回object列表,不返回子目录列表 373 | * 1->只返回子目录列表,不返回object列表 374 | * 2->同时返回子目录列表和object列表 375 | * @param array $opt (Optional) 376 | * start : 主要用于翻页功能,用法同mysql中start的用法 377 | * limit : 主要用于翻页功能,用法同mysql中limit的用法 378 | * @throws BCS_Exception 379 | * @return BCS_ResponseCore 380 | */ 381 | public function list_object_by_dir($bucket, $dir = '/', $list_model = 2, $opt = array()) { 382 | $this->assertParameterArray ( $opt ); 383 | $opt [self::BUCKET] = $bucket; 384 | if (empty ( $opt [self::BUCKET] )) { 385 | throw new BCS_Exception ( "Bucket should not be empty, please check", - 1 ); 386 | } 387 | $opt [self::METHOD] = 'GET'; 388 | $opt [self::OBJECT] = '/'; 389 | $opt [self::QUERY_STRING] = array (); 390 | if (isset ( $opt ['start'] ) && is_int ( $opt ['start'] )) { 391 | $opt [self::QUERY_STRING] ['start'] = $opt ['start']; 392 | } 393 | if (isset ( $opt ['limit'] ) && is_int ( $opt ['limit'] )) { 394 | $opt [self::QUERY_STRING] ['limit'] = $opt ['limit']; 395 | } 396 | 397 | $opt [self::QUERY_STRING] ['prefix'] = rawurlencode ( $dir ); 398 | $opt [self::QUERY_STRING] ['dir'] = $list_model; 399 | 400 | $response = $this->authenticate ( $opt ); 401 | $this->log ( $response->isOK () ? "List object success!" : "Lit object failed! Response: [" . $response->body . "]", $opt ); 402 | return $response; 403 | } 404 | 405 | /** 406 | * 上传文件 407 | * @param string $bucket (Required) 408 | * @param string $object (Required) 409 | * @param string $file (Required); 需要上传的文件的文件路径 410 | * @param array $opt (Optional) 411 | * filename - Optional; 指定文件名 412 | * acl - Optional ; 上传文件的acl,只能使用acl_type 413 | * seekTo - Optional; 上传文件的偏移位置 414 | * length - Optional; 待上传长度 415 | * @return BCS_ResponseCore 416 | */ 417 | public function create_object($bucket, $object, $file, $opt = array()) { 418 | $this->assertParameterArray ( $opt ); 419 | $opt [self::BUCKET] = $bucket; 420 | $opt [self::OBJECT] = $object; 421 | $opt ['fileUpload'] = $file; 422 | $opt [self::METHOD] = 'PUT'; 423 | if (isset ( $opt ['acl'] )) { 424 | if (in_array ( $opt ['acl'], self::$ACL_TYPES )) { 425 | self::set_header_into_opt ( "x-bs-acl", $opt ['acl'], $opt ); 426 | } else { 427 | throw new BCS_Exception ( "Invalid acl string, it should be acl_type", - 1 ); 428 | } 429 | unset ( $opt ['acl'] ); 430 | } 431 | if (isset ( $opt ['filename'] )) { 432 | self::set_header_into_opt ( "Content-Disposition", 'attachment; filename=' . $opt ['filename'], $opt ); 433 | } 434 | $response = $this->authenticate ( $opt ); 435 | $this->log ( $response->isOK () ? "Create object[$object] file[$file] success!" : "Create object[$object] file[$file] failed! Response: [" . $response->body . "] Logid[" . $response->header ["x-bs-request-id"] . "]", $opt ); 436 | return $response; 437 | } 438 | 439 | /** 440 | * 上传文件 441 | * @param string $bucket (Required) 442 | * @param string $object (Required) 443 | * @param string $file (Required); 需要上传的文件的文件路径 444 | * @param array $opt (Optional) 445 | * filename - Optional; 指定文件名 446 | * acl - Optional ; 上传文件的acl,只能使用acl_type 447 | * @return BCS_ResponseCore 448 | */ 449 | public function create_object_by_content($bucket, $object, $content, $opt = array()) { 450 | $this->assertParameterArray ( $opt ); 451 | $opt [self::BUCKET] = $bucket; 452 | $opt [self::OBJECT] = $object; 453 | $opt [self::METHOD] = 'PUT'; 454 | if ($content !== NULL && is_string ( $content )) { 455 | $opt ['content'] = $content; 456 | } else { 457 | throw new BCS_Exception ( "Invalid object content, please check.", - 1 ); 458 | } 459 | if (isset ( $opt ['acl'] )) { 460 | if (in_array ( $opt ['acl'], self::$ACL_TYPES )) { 461 | self::set_header_into_opt ( "x-bs-acl", $opt ['acl'], $opt ); 462 | } else { 463 | throw new BCS_Exception ( "Invalid acl string, it should be acl_type", - 1 ); 464 | } 465 | unset ( $opt ['acl'] ); 466 | } 467 | if (isset ( $opt ['filename'] )) { 468 | self::set_header_into_opt ( "Content-Disposition", 'attachment; filename=' . $opt ['filename'], $opt ); 469 | } 470 | $response = $this->authenticate ( $opt ); 471 | $this->log ( $response->isOK () ? "Create object[$object] success!" : "Create object[$object] failed! Response: [" . $response->body . "] Logid[" . $response->header ["x-bs-request-id"] . "]", $opt ); 472 | return $response; 473 | } 474 | 475 | /** 476 | * 通过superfile的方式上传文件 477 | * @param string $bucket (Required) 478 | * @param string $object (Required) 479 | * @param string $file (Required); 需要上传的文件的文件路径 480 | * @param array $opt (Optional) 481 | * filename - Optional; 指定文件名 482 | * sub_object_size - Optional; 指定子文件的划分大小,单位B,建议以256KB为单位进行子object划分,默认为1MB进行划分 483 | * @return BCS_ResponseCore 484 | */ 485 | public function create_object_superfile($bucket, $object, $file, $opt = array()) { 486 | if (isset ( $opt ['length'] ) || isset ( $opt ['seekTo'] )) { 487 | throw new BCS_Exception ( "Temporary unsupport opt of length and seekTo of superfile.", - 1 ); 488 | } 489 | //$opt array 490 | $this->assertParameterArray ( $opt ); 491 | $opt [self::BUCKET] = $bucket; 492 | $opt ['fileUpload'] = $file; 493 | $opt [self::METHOD] = 'PUT'; 494 | if (isset ( $opt ['acl'] )) { 495 | if (in_array ( $opt ['acl'], self::$ACL_TYPES )) { 496 | self::set_header_into_opt ( "x-bs-acl", $opt ['acl'], $opt ); 497 | } else { 498 | throw new BCS_Exception ( "Invalid acl string, it should be acl_type", - 1 ); 499 | } 500 | unset ( $opt ['acl'] ); 501 | } 502 | //切片上传 503 | if (! file_exists ( $opt ['fileUpload'] )) { 504 | throw new BCS_Exception ( 'File not found!', - 1 ); 505 | } 506 | $fileSize = filesize ( $opt ['fileUpload'] ); 507 | $sub_object_size = 1024 * 1024; //default 1MB 508 | if (defined ( "BCS_SUPERFILE_SLICE_SIZE" )) { 509 | $sub_object_size = BCS_SUPERFILE_SLICE_SIZE; 510 | } 511 | if (isset ( $opt ["sub_object_size"] )) { 512 | if (is_int ( $opt ["sub_object_size"] ) && $opt ["sub_object_size"] > 0) { 513 | $sub_object_size = $opt ["sub_object_size"]; 514 | } else { 515 | throw new BCS_Exception ( "Param [sub_object_size] invalid ,please check!", - 1 ); 516 | } 517 | } 518 | $sliceNum = intval ( ceil ( $fileSize / $sub_object_size ) ); 519 | $this->log ( "File[" . $opt ['fileUpload'] . "], size=[$fileSize], sub_object_size=[$sub_object_size], sub_object_num=[$sliceNum]", $opt ); 520 | $object_list = array ( 521 | 'object_list' => array () ); 522 | for($i = 0; $i < $sliceNum; $i ++) { 523 | //send slice 524 | $opt ['seekTo'] = $i * $sub_object_size; 525 | 526 | if (($i + 1) === $sliceNum) { 527 | //last sub object 528 | $opt ['length'] = (0 === $fileSize % $sub_object_size) ? $sub_object_size : $fileSize % $sub_object_size; 529 | } else { 530 | $opt ['length'] = $sub_object_size; 531 | } 532 | $opt [self::OBJECT] = $object . BCS_SUPERFILE_POSTFIX . $i; 533 | $object_list ['object_list'] ['part_' . $i] = array (); 534 | $object_list ['object_list'] ['part_' . $i] ['url'] = 'bs://' . $bucket . $opt [self::OBJECT]; 535 | $this->log ( "Begin to upload Sub-object[" . $opt [self::OBJECT] . "][$i/$sliceNum][seekto:" . $opt ['seekTo'] . "][Length:" . $opt ['length'] . "]", $opt ); 536 | $response = $this->create_object ( $bucket, $opt [self::OBJECT], $file, $opt ); 537 | if ($response->isOK ()) { 538 | $this->log ( "Sub-object upload[" . $opt [self::OBJECT] . "][$i/$sliceNum][seekto:" . $opt ['seekTo'] . "][Length:" . $opt ['length'] . "]success! ", $opt ); 539 | $object_list ['object_list'] ['part_' . $i] ['etag'] = $response->header ['Content-MD5']; 540 | continue; 541 | } else { 542 | $this->log ( "Sub-object upload[" . $opt [self::OBJECT] . "][$i/$sliceNum] failed! ", $opt ); 543 | return $response; 544 | } 545 | } 546 | //将子文件分片的列表构造成 superfile 547 | unset ( $opt ['fileUpload'] ); 548 | unset ( $opt ['length'] ); 549 | unset ( $opt ['seekTo'] ); 550 | $opt ['content'] = self::array_to_json ( $object_list ); 551 | $opt [self::QUERY_STRING] = array ( 552 | "superfile" => 1 ); 553 | $opt [self::OBJECT] = $object; 554 | if (isset ( $opt ['filename'] )) { 555 | self::set_header_into_opt ( "Content-Disposition", 'attachment; filename=' . $opt ['filename'], $opt ); 556 | } 557 | $response = $this->authenticate ( $opt ); 558 | $this->log ( $response->isOK () ? "Create object-superfile success!" : "Create object-superfile failed! Response: [" . $response->body . "]", $opt ); 559 | return $response; 560 | } 561 | 562 | /** 563 | * 将目录中的所有文件进行上传,每个文件为单独object,object命名方式下详: 564 | * 如有 /home/worker/a/b/c.txt 需上传目录为$dir=/home/worker/a 565 | * object命令方式为 566 | * 1. object默认命名方式为 “子目录名 +文件名”,如上述文件c.txt,默认为 '/b/c.txt' 567 | * 2. 增强命名模式,在$opt中有可选参数进行配置 568 | * 举例说明 :prefix . has_sub_directory?"/b":"" . '/c.txt' 569 | * @param string $bucket (Required) 570 | * @param string $dir (Required) 571 | * @param array $opt(Optional) 572 | * string prefix 文件object前缀 573 | * boolean has_sub_directory(default=true) object命名中是否携带文件的子目录结构,若置为false,请确认待上传的目录和所有子目录中没有重名文件,否则会产生object覆盖问题 574 | * BaiduBCS::IMPORT_BCS_PRE_FILTER 用户可自定义上传文件前的操作函数 575 | * 1. 函数参数列表顺序需为 ($bucket,$object,$file,&$opt),注意$opt为upload_directory函数传入的$opt的拷贝,只对当前object生效 576 | * 2. 函数返回值必须为boolean,当true该文件进行上传,若false跳过上传 577 | * 3. 如果函数返回false,将不会进行post_filter的调用 578 | * BaiduBCS::IMPORT_BCS_POST_FILTER 用户可自定义上传文件后的操作函数 579 | * 1. 函数参数列表顺序需为 ($bucket,$object,$file,&$opt,$response),注意$opt为upload_directory函数传入的$opt的拷贝,只对当前object生效 580 | * 2. 函数返回值无要求 581 | * string seek_object 用户断点续传,需要为object名称,如果该object在目录中不存在,抛出异常,若存在则将该object和此后的object进行上传 582 | * string seek_object_id 作用同seek_object,只需要传入上传过程中日志中展示的[a/b]中object序号即可,注意object序号是以1开始计算的 583 | * @return array 数组形式的上传结果 584 | * 'success' => int 上传成功的文件数目 585 | * 'skipped' => int 被跳过的文件 586 | * 'failed' => array() 上传失败的文件 587 | * 588 | */ 589 | public function upload_directory($bucket, $dir, $opt = array()) { 590 | $this->assertParameterArray ( $opt ); 591 | if (! is_dir ( $dir )) { 592 | throw new BCS_Exception ( "$dir is not a dir!", - 1 ); 593 | } 594 | $result = array ( 595 | "success" => 0, 596 | "failed" => array (), 597 | "skipped" => 0 ); 598 | $prefix = ""; 599 | if (isset ( $opt ['prefix'] )) { 600 | $prefix = $opt ['prefix']; 601 | } 602 | $has_sub_directory = true; 603 | if (isset ( $opt ['has_sub_directory'] ) && is_bool ( $opt ['has_sub_directory'] )) { 604 | $has_sub_directory = $opt ['has_sub_directory']; 605 | } 606 | //获取文件树和构造object名 607 | $file_tree = self::get_filetree ( $dir ); 608 | $objects = array (); 609 | foreach ( $file_tree as $file ) { 610 | $object = $has_sub_directory == true ? substr ( $file, strlen ( $dir ) ) : "/" . basename ( $file ); 611 | $objects [$prefix . $object] = $file; 612 | } 613 | $objectCount = count ( $objects ); 614 | $before_upload_log = "Upload directory: bucket[$bucket] upload_dir[$dir] file_sum[$objectCount]"; 615 | if (isset ( $opt ["seek_object_id"] )) { 616 | $before_upload_log .= " seek_object_id[" . $opt ["seek_object_id"] . "/$objectCount]"; 617 | } 618 | if (isset ( $opt ["seek_object"] )) { 619 | $before_upload_log .= " seek_object[" . $opt ["seek_object"] . "]"; 620 | } 621 | $this->log ( $before_upload_log, $opt ); 622 | //查看是否需要查询断点,进行断点续传 623 | if (isset ( $opt ["seek_object_id"] ) && isset ( $opt ["seek_object"] )) { 624 | throw new BCS_Exception ( "Can not set see_object_id and seek_object at the same time!", - 1 ); 625 | } 626 | 627 | $num = 1; 628 | if (isset ( $opt ["seek_object"] )) { 629 | if (isset ( $objects [$opt ["seek_object"]] )) { 630 | foreach ( $objects as $object => $file ) { 631 | if ($object != $opt ["seek_object"]) { 632 | //当非断点文件,该object已完成上传 633 | $this->log ( "Seeking[" . $opt ["seek_object"] . "]. Skip id[$num/$objectCount]object[$object]file[$file].", $opt ); 634 | //$result ['skipped'] [] = "[$num/$objectCount] " . $file; 635 | $result ['skipped'] ++; 636 | unset ( $objects [$object] ); 637 | } else { 638 | //当找到断点文件,停止循环,从断点文件重新上传 639 | //当非断点文件,该object已完成上传 640 | $this->log ( "Found seek id[$num/$objectCount]object[$object]file[$file], begin from here.", $opt ); 641 | break; 642 | } 643 | $num ++; 644 | } 645 | } else { 646 | throw new BCS_Exception ( "Can not find you seek object, please check!", - 1 ); 647 | } 648 | } 649 | if (isset ( $opt ["seek_object_id"] )) { 650 | if (is_int ( $opt ["seek_object_id"] ) && $opt ["seek_object_id"] <= $objectCount) { 651 | foreach ( $objects as $object => $file ) { 652 | if ($num < $opt ["seek_object_id"]) { 653 | $this->log ( "Seeking object of [" . $opt ["seek_object_id"] . "/$objectCount]. Skip id[$num/$objectCount]object[$object]file[$file].", $opt ); 654 | //$result ['skipped'] [] = "[$num/$objectCount] " . $file; 655 | $result ['skipped'] ++; 656 | unset ( $objects [$object] ); 657 | } else { 658 | break; 659 | } 660 | $num ++; 661 | } 662 | } else { 663 | throw new BCS_Exception ( "Param seek_object_id not valid, please check!", - 1 ); 664 | } 665 | } 666 | //上传objects 667 | $objectCount = count ( $objects ); 668 | foreach ( $objects as $object => $file ) { 669 | $tmp_opt = array_merge ( $opt ); 670 | if (isset ( $opt [self::IMPORT_BCS_PRE_FILTER] ) && function_exists ( $opt [self::IMPORT_BCS_PRE_FILTER] )) { 671 | $bolRes = $opt [self::IMPORT_BCS_PRE_FILTER] ( $bucket, $object, $file, $tmp_opt ); 672 | if ($bolRes !== true) { 673 | $this->log ( "User pre_filter_function return un-true. Skip id[$num/$objectCount]object[$object]file[$file].", $opt ); 674 | //$result ['skipped'] [] = "id[$num/$objectCount]object[$object]file[$file]"; 675 | $result ['skipped'] ++; 676 | $num ++; 677 | continue; 678 | } 679 | } 680 | try { 681 | $response = $this->create_object ( $bucket, $object, $file, $tmp_opt ); 682 | } catch ( Exception $e ) { 683 | $this->log ( $e->getMessage (), $opt ); 684 | $this->log ( "Upload Failed id[$num/$objectCount]object[$object]file[$file].", $opt ); 685 | $num ++; 686 | continue; 687 | } 688 | if ($response->isOK ()) { 689 | $result ["success"] ++; 690 | $this->log ( "Upload Success id[$num/$objectCount]object[$object]file[$file].", $opt ); 691 | } else { 692 | $result ["failed"] [] = "id[$num/$objectCount]object[$object]file[$file]"; 693 | $this->log ( "Upload Failed id[$num/$objectCount]object[$object]file[$file].", $opt ); 694 | } 695 | if (isset ( $opt [self::IMPORT_BCS_POST_FILTER] ) && function_exists ( $opt [self::IMPORT_BCS_POST_FILTER] )) { 696 | $opt [self::IMPORT_BCS_POST_FILTER] ( $bucket, $object, $file, $tmp_opt, $response ); 697 | } 698 | $num ++; 699 | } 700 | //打印日志并返回结果数组 701 | $result_str = "\r\n\r\nUpload $dir to $bucket finished!\r\n"; 702 | $result_str .= "**********************************************************\r\n"; 703 | $result_str .= "**********************Result Summary**********************\r\n"; 704 | $result_str .= "**********************************************************\r\n"; 705 | $result_str .= "Upload directory : [$dir]\r\n"; 706 | $result_str .= "File num : [$objectCount]\r\n"; 707 | $result_str .= "Success: \r\n\tNum: " . $result ["success"] . "\r\n"; 708 | $result_str .= "Skipped:\r\n\tNum:" . $result ["skipped"] . "\r\n"; 709 | // foreach ( $result ["skipped"] as $skip ) { 710 | // $result_str .= "\t$skip\r\n"; 711 | // } 712 | $result_str .= "Failed:\r\n\tNum:" . count ( $result ["failed"] ) . "\r\n"; 713 | foreach ( $result ["failed"] as $fail ) { 714 | $result_str .= "\t$fail\r\n"; 715 | } 716 | if (isset ( $opt [self::IMPORT_BCS_LOG_METHOD] )) { 717 | $this->log ( $result_str, $opt ); 718 | } else { 719 | echo $result_str; 720 | } 721 | return $result; 722 | } 723 | 724 | /** 725 | * 通过此方法以拷贝的方式创建object,object来源为$source 726 | * @param array $source (Required) object 来源 727 | * bucket(Required) 728 | * object(Required) 729 | * @param array $dest (Required) 待拷贝的目标object 730 | * bucket(Required) 731 | * object(Required) 732 | * @param array $opt (Optional) 733 | * source_tag 指定拷贝对象的版本号 734 | * @throws BCS_Exception 735 | * @return BCS_ResponseCore 736 | */ 737 | public function copy_object($source, $dest, $opt = array()) { 738 | $this->assertParameterArray ( $opt ); 739 | //valid source and dest 740 | if (empty ( $source ) || ! is_array ( $source ) || ! isset ( $source [self::BUCKET] ) || ! isset ( $source [self::OBJECT] )) { 741 | throw new BCS_Exception ( '$source invalid, please check!', - 1 ); 742 | } 743 | if (empty ( $dest ) || ! is_array ( $dest ) || ! isset ( $dest [self::BUCKET] ) || ! isset ( $dest [self::OBJECT] ) || ! self::validate_bucket ( $dest [self::BUCKET] ) || ! self::validate_object ( $dest [self::OBJECT] )) { 744 | throw new BCS_Exception ( '$dest invalid, please check!', - 1 ); 745 | } 746 | $opt [self::BUCKET] = $dest [self::BUCKET]; 747 | $opt [self::OBJECT] = $dest [self::OBJECT]; 748 | $opt [self::METHOD] = 'PUT'; 749 | self::set_header_into_opt ( 'x-bs-copy-source', 'bs://' . $source [self::BUCKET] . $source [self::OBJECT], $opt ); 750 | if (isset ( $opt ['source_tag'] )) { 751 | self::set_header_into_opt ( 'x-bs-copy-source-tag', $opt ['source_tag'], $opt ); 752 | } 753 | $response = $this->authenticate ( $opt ); 754 | $this->log ( $response->isOK () ? "Copy object success!" : "Copy object failed! Response: [" . $response->body . "]", $opt ); 755 | return $response; 756 | } 757 | 758 | /** 759 | * 设置object的meta信息 760 | * @param string $bucket (Required) 761 | * @param string $object (Required) 762 | * @param array $opt (Optional) 763 | * 目前支持的meta信息如下: 764 | * Content-Type 765 | * Cache-Control 766 | * Content-Disposition 767 | * Content-Encoding 768 | * Content-MD5 769 | * Expires 770 | * @return BCS_ResponseCore 771 | */ 772 | public function set_object_meta($bucket, $object, $meta, $opt = array()) { 773 | $this->assertParameterArray ( $opt ); 774 | $this->assertParameterArray ( $meta ); 775 | $opt [self::BUCKET] = $bucket; 776 | $opt [self::OBJECT] = $object; 777 | $opt [self::METHOD] = 'PUT'; 778 | //利用copy_object接口来设置meta信息 779 | $source = "bs://$bucket$object"; 780 | if (empty ( $meta )) { 781 | throw new BCS_Exception ( '$meta can not be empty! And $meta must be array.', - 1 ); 782 | } 783 | foreach ( $meta as $header => $value ) { 784 | self::set_header_into_opt ( $header, $value, $opt ); 785 | } 786 | $source = array ( 787 | self::BUCKET => $bucket, 788 | self::OBJECT => $object ); 789 | $response = $this->copy_object ( $source, $source, $opt ); 790 | $this->log ( $response->isOK () ? "Set object meta success!" : "Set object meta failed! Response: [" . $response->body . "]", $opt ); 791 | return $response; 792 | } 793 | 794 | /** 795 | * 获取object的acl 796 | * @param string $bucket (Required) 797 | * @param string $object (Required) 798 | * @param array $opt (Optional) 799 | * @throws BCS_Exception 800 | * @return BCS_ResponseCore 801 | */ 802 | public function get_object_acl($bucket, $object, $opt = array()) { 803 | $this->assertParameterArray ( $opt ); 804 | $opt [self::BUCKET] = $bucket; 805 | $opt [self::METHOD] = 'GET'; 806 | $opt [self::OBJECT] = $object; 807 | $opt [self::QUERY_STRING] = array ( 808 | self::ACL => 1 ); 809 | $response = $this->authenticate ( $opt ); 810 | $this->log ( $response->isOK () ? "Get object acl success!" : "Get object acl failed! Response: [" . $response->body . "]", $opt ); 811 | return $response; 812 | } 813 | 814 | /** 815 | * 设置object的acl,有三种模式, 816 | * (1).设置详细json格式的acl; 817 | * a. $acl 为json的array 818 | * b. $acl 为json的string 819 | * (2).通过acl_type字段进行设置 820 | * a. $acl 为BaiduBCS::$ACL_ACTIONS中的字段 821 | * @param string $bucket (Required) 822 | * @param string $object (Required) 823 | * @param string|array $acl (Required) 824 | * @param array $opt (Optional) 825 | * @return BCS_ResponseCore 826 | */ 827 | public function set_object_acl($bucket, $object, $acl, $opt = array()) { 828 | $this->assertParameterArray ( $opt ); 829 | //analyze acl 830 | $result = $this->analyze_user_acl ( $acl ); 831 | $opt = array_merge ( $opt, $result ); 832 | $opt [self::BUCKET] = $bucket; 833 | $opt [self::METHOD] = 'PUT'; 834 | $opt [self::OBJECT] = $object; 835 | $opt [self::QUERY_STRING] = array ( 836 | self::ACL => 1 ); 837 | $response = $this->authenticate ( $opt ); 838 | $this->log ( $response->isOK () ? "Set object acl success!" : "Set object acl failed! Response: [" . $response->body . "]", $opt ); 839 | return $response; 840 | } 841 | 842 | /** 843 | * 删除object 844 | * @param string $bucket (Required) 845 | * @param string $object (Required) 846 | * @param array $opt (Optional) 847 | * @throws BCS_Exception 848 | * @return BCS_ResponseCore 849 | */ 850 | public function delete_object($bucket, $object, $opt = array()) { 851 | $this->assertParameterArray ( $opt ); 852 | $opt [self::BUCKET] = $bucket; 853 | $opt [self::METHOD] = 'DELETE'; 854 | $opt [self::OBJECT] = $object; 855 | $response = $this->authenticate ( $opt ); 856 | $this->log ( $response->isOK () ? "Delete object success!" : "Delete object failed! Response: [" . $response->body . "]", $opt ); 857 | return $response; 858 | } 859 | 860 | /** 861 | * 判断object是否存在 862 | * @param string $bucket (Required) 863 | * @param string $object (Required) 864 | * @param array $opt (Optional) 865 | * @throws BCS_Exception 866 | * @return boolean true|boolean false|BCS_ResponseCore 867 | * true:object存在 868 | * false:不存在 869 | * BCS_ResponseCore其他错误 870 | */ 871 | public function is_object_exist($bucket, $object, $opt = array()) { 872 | $this->assertParameterArray ( $opt ); 873 | $opt [self::BUCKET] = $bucket; 874 | $opt [self::METHOD] = 'HEAD'; 875 | $opt [self::OBJECT] = $object; 876 | $response = $this->get_object_info ( $bucket, $object, $opt ); 877 | if ($response->isOK ()) { 878 | return true; 879 | } elseif ($response->status === 404) { 880 | return false; 881 | } 882 | return $response; 883 | } 884 | 885 | /** 886 | * 获取文件信息,发送的为HTTP HEAD请求,文件信息都在http response的header中,不会提取文件的内容 887 | * @param string $bucket (Required) 888 | * @param string $object (Required) 889 | * @param array $opt (Optional) 890 | * @throws BCS_Exception 891 | * @return array BCS_ResponseCore 892 | */ 893 | public function get_object_info($bucket, $object, $opt = array()) { 894 | $this->assertParameterArray ( $opt ); 895 | $opt [self::BUCKET] = $bucket; 896 | $opt [self::METHOD] = 'HEAD'; 897 | $opt [self::OBJECT] = $object; 898 | $response = $this->authenticate ( $opt ); 899 | $this->log ( $response->isOK () ? "Get object info success!" : "Get object info failed! Response: [" . $response->body . "]", $opt ); 900 | return $response; 901 | } 902 | 903 | /** 904 | * 下载object 905 | * @param string $bucket (Required) 906 | * @param string $object (Required) 907 | * @param array $opt (Optional) 908 | * fileWriteTo (Optional)直接将请求结果写入该文件,如果fileWriteTo文件存在,sdk进行重命名再存储 909 | * @throws BCS_Exception 910 | * @return BCS_ResponseCore 911 | */ 912 | public function get_object($bucket, $object, $opt = array()) { 913 | $this->assertParameterArray ( $opt ); 914 | //若fileWriteTo待写入的文件已经存在,需要进行重命名 915 | if (isset ( $opt ["fileWriteTo"] ) && file_exists ( $opt ["fileWriteTo"] )) { 916 | $original_file_write_to = $opt ["fileWriteTo"]; 917 | $arr = explode ( DIRECTORY_SEPARATOR, $opt ["fileWriteTo"] ); 918 | $file_name = $arr [count ( $arr ) - 1]; 919 | $num = 1; 920 | while ( file_exists ( $opt ["fileWriteTo"] ) ) { 921 | $new_name_arr = explode ( ".", $file_name ); 922 | if (count ( $new_name_arr ) > 1) { 923 | $new_name_arr [count ( $new_name_arr ) - 2] .= " ($num)"; 924 | } else { 925 | $new_name_arr [0] .= " ($num)"; 926 | } 927 | $arr [count ( $arr ) - 1] = implode ( ".", $new_name_arr ); 928 | $opt ["fileWriteTo"] = implode ( DIRECTORY_SEPARATOR, $arr ); 929 | $num ++; 930 | } 931 | $this->log ( "[$original_file_write_to] already exist, rename it to [" . $opt ["fileWriteTo"] . "]", $opt ); 932 | } 933 | $opt [self::BUCKET] = $bucket; 934 | $opt [self::METHOD] = 'GET'; 935 | $opt [self::OBJECT] = $object; 936 | $response = $this->authenticate ( $opt ); 937 | $this->log ( $response->isOK () ? "Get object success!" : "Get object failed! Response: [" . $response->body . "]", $opt ); 938 | if (! $response->isOK () && isset ( $opt ["fileWriteTo"] )) { 939 | unlink ( $opt ["fileWriteTo"] ); 940 | } 941 | return $response; 942 | } 943 | 944 | /** 945 | * 生成签名链接 946 | */ 947 | private function generate_user_url($method, $bucket, $object, $opt = array()) { 948 | $opt [self::AK] = $this->ak; 949 | $opt [self::SK] = $this->sk; 950 | $opt [self::BUCKET] = $bucket; 951 | $opt [self::METHOD] = $method; 952 | $opt [self::OBJECT] = $object; 953 | $opt [self::QUERY_STRING] = array (); 954 | if (isset ( $opt ["time"] )) { 955 | $opt [self::QUERY_STRING] ["time"] = $opt ["time"]; 956 | } 957 | if (isset ( $opt ["size"] )) { 958 | $opt [self::QUERY_STRING] ["size"] = $opt ["size"]; 959 | } 960 | return $this->format_url ( $opt ); 961 | } 962 | 963 | /** 964 | * 生成get_object的url 965 | * @param string $bucket (Required) 966 | * @param string $object (Required) 967 | * return false| string url 968 | */ 969 | public function generate_get_object_url($bucket, $object, $opt = array()) { 970 | $this->assertParameterArray ( $opt ); 971 | return $this->generate_user_url ( "GET", $bucket, $object, $opt ); 972 | } 973 | 974 | /** 975 | * 生成put_object的url 976 | * @param string $bucket (Required) 977 | * @param string $object (Required) 978 | * return false| string url 979 | */ 980 | public function generate_put_object_url($bucket, $object, $opt = array()) { 981 | $this->assertParameterArray ( $opt ); 982 | return $this->generate_user_url ( "PUT", $bucket, $object, $opt ); 983 | } 984 | 985 | /** 986 | * 生成post_object的url 987 | * @param string $bucket (Required) 988 | * @param string $object (Required) 989 | * return false| string url 990 | */ 991 | public function generate_post_object_url($bucket, $object, $opt = array()) { 992 | $this->assertParameterArray ( $opt ); 993 | return $this->generate_user_url ( "POST", $bucket, $object, $opt ); 994 | } 995 | 996 | /** 997 | * 生成delete_object的url 998 | * @param string $bucket (Required) 999 | * @param string $object (Required) 1000 | * return false| string url 1001 | */ 1002 | public function generate_delete_object_url($bucket, $object, $opt = array()) { 1003 | $this->assertParameterArray ( $opt ); 1004 | return $this->generate_user_url ( "DELETE", $bucket, $object, $opt ); 1005 | } 1006 | 1007 | /** 1008 | * 生成head_object的url 1009 | * @param string $bucket (Required) 1010 | * @param string $object (Required) 1011 | * return false| string url 1012 | */ 1013 | public function generate_head_object_url($bucket, $object, $opt = array()) { 1014 | $this->assertParameterArray ( $opt ); 1015 | return $this->generate_user_url ( "HEAD", $bucket, $object, $opt ); 1016 | } 1017 | 1018 | /** 1019 | * @return the $use_ssl 1020 | */ 1021 | public function getUse_ssl() { 1022 | return $this->use_ssl; 1023 | } 1024 | 1025 | /** 1026 | * @param boolean $use_ssl 1027 | */ 1028 | public function setUse_ssl($use_ssl) { 1029 | $this->use_ssl = $use_ssl; 1030 | } 1031 | 1032 | /** 1033 | * 校验bucket是否合法,bucket规范 1034 | * 1. 由小写字母,数字和横线'-'组成,长度为6~63位 1035 | * 2. 不能以数字作为Bucket开头 1036 | * 3. 不能以'-'作为Bucket的开头或者结尾 1037 | * @param string $bucket 1038 | * @return boolean 1039 | */ 1040 | public static function validate_bucket($bucket) { 1041 | //bucket 正则 1042 | $pattern1 = '/^[a-z][-a-z0-9]{4,61}[a-z0-9]$/'; 1043 | if (! preg_match ( $pattern1, $bucket )) { 1044 | return false; 1045 | } 1046 | return true; 1047 | } 1048 | 1049 | /** 1050 | * 校验object是否合法,object命名规范 1051 | * 1. object必须以'/'开头 1052 | * @param string $object 1053 | * @return boolean 1054 | */ 1055 | public static function validate_object($object) { 1056 | $pattern = '/^\//'; 1057 | if (empty ( $object ) || ! preg_match ( $pattern, $object )) { 1058 | return false; 1059 | } 1060 | return true; 1061 | } 1062 | 1063 | /** 1064 | * 将常用set http-header的动作抽离出来 1065 | * @param string $header 1066 | * @param string $value 1067 | * @param array $opt 1068 | * @throws BCS_Exception 1069 | * @return void 1070 | */ 1071 | private static function set_header_into_opt($header, $value, &$opt) { 1072 | if (isset ( $opt [self::HEADERS] )) { 1073 | if (! is_array ( $opt [self::HEADERS] )) { 1074 | trigger_error ( 'Invalid $opt[\'headers\'], please check.' ); 1075 | throw new BCS_Exception ( 'Invalid $opt[\'headers\'], please check.', - 1 ); 1076 | } 1077 | } else { 1078 | $opt [self::HEADERS] = array (); 1079 | } 1080 | $opt [self::HEADERS] [$header] = $value; 1081 | } 1082 | 1083 | /** 1084 | * 使用特定function对数组中所有元素做处理 1085 | * @param string &$array 要处理的字符串 1086 | * @param string $function 要执行的函数 1087 | * @param boolean $apply_to_keys_also 是否也应用到key上 1088 | */ 1089 | private static function array_recursive(&$array, $function, $apply_to_keys_also = false) { 1090 | foreach ( $array as $key => $value ) { 1091 | if (is_array ( $value )) { 1092 | self::array_recursive ( $array [$key], $function, $apply_to_keys_also ); 1093 | } else { 1094 | $array [$key] = $function ( $value ); 1095 | } 1096 | 1097 | if ($apply_to_keys_also && is_string ( $key )) { 1098 | $new_key = $function ( $key ); 1099 | if ($new_key != $key) { 1100 | $array [$new_key] = $array [$key]; 1101 | unset ( $array [$key] ); 1102 | } 1103 | } 1104 | } 1105 | } 1106 | 1107 | /** 1108 | * 由数组构造json字符串,增加了一些特殊处理以支持特殊字符和不同编码的中文 1109 | * @param array $array 1110 | */ 1111 | private static function array_to_json($array) { 1112 | if (! is_array ( $array )) { 1113 | throw new BCS_Exception ( "Param must be array in function array_to_json()", - 1 ); 1114 | } 1115 | self::array_recursive ( $array, 'addslashes', false ); 1116 | self::array_recursive ( $array, 'rawurlencode', false ); 1117 | return rawurldecode ( json_encode ( $array ) ); 1118 | } 1119 | 1120 | /** 1121 | * 根据用户传入的acl,进行相应的处理 1122 | * (1).设置详细json格式的acl; 1123 | * a. $acl 为json的array 1124 | * b. $acl 为json的string 1125 | * (2).通过acl_type字段进行设置 1126 | * @param string|array $acl 1127 | * @throws BCS_Exception 1128 | * @return array 1129 | */ 1130 | private function analyze_user_acl($acl) { 1131 | $result = array (); 1132 | if (is_array ( $acl )) { 1133 | //(1).a 1134 | $result ['content'] = $this->check_user_acl ( $acl ); 1135 | } else if (is_string ( $acl )) { 1136 | if (in_array ( $acl, self::$ACL_TYPES )) { 1137 | //(2).a 1138 | $result ["headers"] = array ( 1139 | "x-bs-acl" => $acl ); 1140 | } else { 1141 | //(1).b 1142 | $result ['content'] = $acl; 1143 | } 1144 | } else { 1145 | throw new BCS_Exception ( "Invalid acl.", - 1 ); 1146 | } 1147 | return $result; 1148 | } 1149 | 1150 | /** 1151 | * 生成签名 1152 | * @param array $opt 1153 | * @return boolean|string 1154 | */ 1155 | private function format_signature($opt) { 1156 | $flags = ""; 1157 | $content = ''; 1158 | if (! isset ( $opt [self::AK] ) || ! isset ( $opt [self::SK] )) { 1159 | trigger_error ( 'ak or sk is not in the array when create factor!' ); 1160 | return false; 1161 | } 1162 | if (isset ( $opt [self::BUCKET] ) && isset ( $opt [self::METHOD] ) && isset ( $opt [self::OBJECT] )) { 1163 | $flags .= 'MBO'; 1164 | $content .= "Method=" . $opt [self::METHOD] . "\n"; //method 1165 | $content .= "Bucket=" . $opt [self::BUCKET] . "\n"; //bucket 1166 | $content .= "Object=" . self::trimUrl ( $opt [self::OBJECT] ) . "\n"; //object 1167 | } else { 1168 | trigger_error ( 'bucket、method and object cann`t be NULL!' ); 1169 | return false; 1170 | } 1171 | if (isset ( $opt ['ip'] )) { 1172 | $flags .= 'I'; 1173 | $content .= "Ip=" . $opt ['ip'] . "\n"; 1174 | } 1175 | if (isset ( $opt ['time'] )) { 1176 | $flags .= 'T'; 1177 | $content .= "Time=" . $opt ['time'] . "\n"; 1178 | } 1179 | if (isset ( $opt ['size'] )) { 1180 | $flags .= 'S'; 1181 | $content .= "Size=" . $opt ['size'] . "\n"; 1182 | } 1183 | $content = $flags . "\n" . $content; 1184 | $sign = base64_encode ( hash_hmac ( 'sha1', $content, $opt [self::SK], true ) ); 1185 | return 'sign=' . $flags . ':' . $opt [self::AK] . ':' . urlencode ( $sign ); 1186 | } 1187 | 1188 | /** 1189 | * 检查用户输入的acl array是否合法,并转为json 1190 | * @param array $acl 1191 | * @throws BCS_Exception 1192 | * @return string acl-json 1193 | */ 1194 | private function check_user_acl($acl) { 1195 | if (! is_array ( $acl )) { 1196 | throw new BCS_Exception ( "Invalid acl array" ); 1197 | } 1198 | foreach ( $acl ['statements'] as $key => $statement ) { 1199 | // user resource action effect must in statement 1200 | if (! isset ( $statement ['user'] ) || ! isset ( $statement ['resource'] ) || ! isset ( $statement ['action'] ) || ! isset ( $statement ['effect'] )) { 1201 | throw new BCS_Exception ( 'Param miss: format acl error, please check your param!' ); 1202 | } 1203 | if (! is_array ( $statement ['user'] ) || ! is_array ( $statement ['resource'] )) { 1204 | throw new BCS_Exception ( 'Param error: user or resource must be array, please check your param!' ); 1205 | } 1206 | if (! is_array ( $statement ['action'] ) || ! count ( array_diff ( $statement ['action'], self::$ACL_ACTIONS ) ) == 0) { 1207 | throw new BCS_Exception ( 'Param error: action, please check your param!' ); 1208 | } 1209 | if (! in_array ( $statement ['effect'], self::$ACL_EFFECTS )) { 1210 | throw new BCS_Exception ( 'Param error: effect, please check your param!' ); 1211 | } 1212 | if (isset ( $statement ['time'] )) { 1213 | if (! is_array ( $statement ['time'] )) { 1214 | throw new BCS_Exception ( 'Param error: time, please check your param!' ); 1215 | } 1216 | } 1217 | } 1218 | 1219 | return self::array_to_json ( $acl ); 1220 | } 1221 | 1222 | /** 1223 | * 构造url 1224 | * @param array $opt 1225 | * @return boolean|string 1226 | */ 1227 | private function format_url($opt) { 1228 | $sign = $this->format_signature ( $opt ); 1229 | if ($sign === false) { 1230 | trigger_error ( "Format signature failed, please check!" ); 1231 | return false; 1232 | } 1233 | $opt ['sign'] = $sign; 1234 | $url = ""; 1235 | $url .= $this->use_ssl ? 'https://' : 'http://'; 1236 | $url .= $this->hostname; 1237 | $url .= '/' . $opt [self::BUCKET]; 1238 | if (isset ( $opt [self::OBJECT] ) && '/' !== $opt [self::OBJECT]) { 1239 | $url .= "/" . rawurlencode ( $opt [self::OBJECT] ); 1240 | } 1241 | $url .= '?' . $sign; 1242 | if (isset ( $opt [self::QUERY_STRING] )) { 1243 | foreach ( $opt [self::QUERY_STRING] as $key => $value ) { 1244 | $url .= '&' . $key . '=' . $value; 1245 | } 1246 | } 1247 | return $url; 1248 | } 1249 | 1250 | /** 1251 | * 将url中 '//' 替换为 '/' 1252 | * @param $url 1253 | * @return string 1254 | */ 1255 | public static function trimUrl($url) { 1256 | $result = str_replace ( "//", "/", $url ); 1257 | while ( $result !== $url ) { 1258 | $url = $result; 1259 | $result = str_replace ( "//", "/", $url ); 1260 | } 1261 | return $result; 1262 | } 1263 | 1264 | /** 1265 | * 获取传入目录的文件列表 1266 | * @param string $dir 文件目录 1267 | * @return array 文件树 1268 | */ 1269 | public static function get_filetree($dir, $file_prefix = "/*") { 1270 | $tree = array (); 1271 | foreach ( glob ( $dir . $file_prefix ) as $single ) { 1272 | if (is_dir ( $single )) { 1273 | $tree = array_merge ( $tree, self::get_filetree ( $single ) ); 1274 | } else { 1275 | $tree [] = $single; 1276 | } 1277 | } 1278 | return $tree; 1279 | } 1280 | 1281 | /** 1282 | * 内置的日志函数,可以根据用户传入的log函数,进行日志输出 1283 | * @param string $log 1284 | * @param array $opt 1285 | */ 1286 | public function log($log, $opt) { 1287 | if (isset ( $opt [self::IMPORT_BCS_LOG_METHOD] ) && function_exists ( $opt [self::IMPORT_BCS_LOG_METHOD] )) { 1288 | $opt [self::IMPORT_BCS_LOG_METHOD] ( $log ); 1289 | } else { 1290 | trigger_error ( $log ); 1291 | } 1292 | } 1293 | 1294 | /** 1295 | * make sure $opt is an array 1296 | * @param $opt 1297 | */ 1298 | private function assertParameterArray($opt) { 1299 | if (! is_array ( $opt )) { 1300 | throw new BCS_Exception ( 'Parameter must be array, please check!', - 1 ); 1301 | } 1302 | } 1303 | } --------------------------------------------------------------------------------