Browse Source

Merge pull request #1 from oott123/master

Thanks a lot.
Zeng Bin 11 years ago
parent
commit
c908d0fa85

+ 2 - 0
.gitignore

@@ -0,0 +1,2 @@
+/cache
+/config.inc.php

+ 6 - 0
.htaccess

@@ -0,0 +1,6 @@
+RewriteEngine On
+RewriteBase /
+RewriteRule ^index\.php$ - [L]
+RewriteCond %{REQUEST_FILENAME} !-f
+RewriteCond %{REQUEST_FILENAME} !-d
+RewriteRule . /index.php [L]

+ 6 - 2
README.md

@@ -1,3 +1,7 @@
-## SaeLayerCDN
+## Layer
 
-一个下午的成果,想法是利用SAE来做CDN。<del>更多说明档以后加上...</del>使用文档在[http://creatist.cn/cdn](http://creatist.cn/cdn)
+forked from [SaeLayerCDN](https://github.com/Slacken/cdn).
+
+灏哠aeLayerCDN绉绘�鍒板叏骞冲彴锛屾墍浠ユ槸鈥淟ayer鈥濊€屼笉鍐嶆槸鈥淪aeLayer鈥濄€�
+
+鐩�墠鏀�寔骞冲彴锛歋AE锛堟柊娴�級銆丅AE锛堢櫨搴︼級銆丟CS锛堢洓澶э級銆佹爣鍑哖HP锛堟湰鍦拌�鍐欙級

+ 3 - 0
app.conf

@@ -0,0 +1,3 @@
+handlers:
+  - check_exist: not_exist
+    script: /index.php

+ 28 - 0
config.sample.inc.php

@@ -0,0 +1,28 @@
+<?php
+	//Layer CDN 设定
+	
+	/**********基本设定**********/
+	define('STATIC_URL','http://www.baidu.com/');	//源站URL
+	define('DOMAIN','layercdn');	//使用云存储时,填写存储空间的名字;使用本地存储时,填写存储的相对路径。
+	define('WELCOME_DOC',TRUE);	//空请求时是否显示欢迎界面
+	
+	/*********防盗链设定*********/
+	define('ALLOW_REGX','.*');	//设置防盗链允许的[域名]正则表达式,此表达式只匹配referer的域名部分
+	//define('ALLOW_REGX','^(best33\.com|.*\.best33\.com|)$');	//允许best33.com,*.best33.com,浏览器直接访问
+	//define('ALLOW_REGX','^(best33\.com|.*\.best33\.com)$');	//允许best33.com,*.best33.com,不允许浏览器直接访问
+	//define('ALLOW_REGX','^(.*)$');	//允许任意,允许浏览器访问
+	//define('ALLOW_REGX','^(.+)$');	//允许任意,但不允许浏览器访问
+	
+	/**********进阶设定**********/
+	define('PURGE_KEY','purge');	//刷新缓存的密码,访问http://domain/PURGE_KEY/path/to/file来刷新缓存。
+	define('MIME','text/html');	//默认MIME类型,可以设为application/octet-stream则对未知项目自动弹出下载。
+	define('DIRECT_EXT','php|asp|htm|html');	//不进入缓存的扩展名,安全起见不要删除PHP。
+	define('NO_LOCATE',false);	//设置后将不进行跳转而采用read读取方式,可能会降低速度并增加流量。仅当遇到问题时启用。
+	define('NO_KEY',true);	//启用后将不再使用一串md5编码的key作为文件名,当想保持文件名一致时启用之。
+	define('NO_SECOND_FLODER',true);	//启用后将不再使用两层文件夹存储缓存,仅在本地环境、NO_KEY为假时有效。
+	define('STATIC_HOST','');	//可以留空,也可以在这里填写你的源站域名,而在STATIC_URL中填写IP,减少域名解析的时间。
+	
+	/**********高级设定**********/
+	//define('RUN_ENV', 'GCS');	//自定义运行环境(如不去掉前面的//则自动判断)可选:BAE/SAE/GCS/LOCAL 请大写
+	//define('CS_AK','dummy');	//自定义云存储空间的Access Token,通常不需要
+	//define('CS_SK','dummy');	//自定义云存储空间的Secret Token,通常不需要

+ 1 - 1
config.yaml

@@ -6,4 +6,4 @@ handle:
 - compress: if ( out_header["Content-type"]=="text/css" ) compress
 - compress: if ( out_header["Content-type"]=="image/jpeg" ) compress
 - compress: if ( out_header["Content-type"]=="image/png" ) compress
-- rewrite: if ( !is_dir() && !is_file() && path ~ "^(.*)$" ) goto "index.php?q=$1"
+- rewrite: if ( !is_dir() && !is_file() && path ~ "^(.*)$" ) goto "index.php"

+ 257 - 36
include/controller.php

@@ -10,6 +10,8 @@ class controller{
 	
 	public $error_type;
 	
+	private $hit = false;
+	
 	public function __construct($request = ''){
 		
 		$this->content_type = 'text/html';
@@ -17,64 +19,269 @@ class controller{
 		$this->succeed = TRUE;
 		
 		$request = ltrim($request,'/');
+		$ext = 'cache';
 		
-		//是否为SAE环境
-		if(!IS_SAE || !class_exists('SaeStorage')){//
-			$this->error_type = 2;
+		//检测环境
+		if(!RUN_ENV){
+			$this->error_type = 'no_run_env';
 			$this->succeed = FALSE;
 		}
 		
 		//请求为空
-		elseif($request === ''){
-			
+		elseif($request === '' && WELCOME_DOC){
 			//显示欢迎页面
-			if(WELCOME_DOC){
-				view::show('welcome');
-				return ;
-			}else{
-				$this->error_type = 1;
-				$this->succeed = FALSE;
-			}
+			view::show('welcome');
+			return ;
 		}
-		
 		else{
-			//匹配文件后缀
-			$temp = array();
-			if(preg_match('/\.(jpg|jpeg|png|pdf|gif|css|js|zip)$/i', $request,$temp)===1){//暂时先就这几种
-				//http://en.wikipedia.org/wiki/Internet_media_type#List_of_common_media_types
-				switch($temp[1]){
-					case 'jpg':{$this->content_type="image/jpeg";}break;
-					case 'gif':{$this->content_type="image/gif";}break;
-					case 'png':{$this->content_type="image/png";}break;
-					case 'css':{$this->content_type="text/css";}break;
-					case 'js':{$this->content_type="text/javascript";}break;
+			//检查防盗链
+			$referer = isset($_SERVER['HTTP_REFERER'])?$_SERVER['HTTP_REFERER']:'';
+			@$referer = parse_url($referer);
+			$referer = isset($referer['host'])?$referer['host']:'';
+			if(ALLOW_REGX && !preg_match('/'.ALLOW_REGX.'/i',$referer)){
+				$this->error_type = 'not_allowed_domain';
+				$this->succeed = FALSE;
+			}else{
+				//匹配文件后缀
+				$mime_types = array(
+					'3gp' => 'video/3gpp',
+					'ai' => 'application/postscript',
+					'aif' => 'audio/x-aiff',
+					'aifc' => 'audio/x-aiff',
+					'aiff' => 'audio/x-aiff',
+					'asc' => 'text/plain',
+					'atom' => 'application/atom+xml',
+					'au' => 'audio/basic',
+					'avi' => 'video/x-msvideo',
+					'bcpio' => 'application/x-bcpio',
+					'bin' => 'application/octet-stream',
+					'bmp' => 'image/bmp',
+					'cdf' => 'application/x-netcdf',
+					'cgm' => 'image/cgm',
+					'class' => 'application/octet-stream',
+					'cpio' => 'application/x-cpio',
+					'cpt' => 'application/mac-compactpro',
+					'csh' => 'application/x-csh',
+					'css' => 'text/css',
+					'dcr' => 'application/x-director',
+					'dif' => 'video/x-dv',
+					'dir' => 'application/x-director',
+					'djv' => 'image/vnd.djvu',
+					'djvu' => 'image/vnd.djvu',
+					'dll' => 'application/octet-stream',
+					'dmg' => 'application/octet-stream',
+					'dms' => 'application/octet-stream',
+					'doc' => 'application/msword',
+					'dtd' => 'application/xml-dtd',
+					'dv' => 'video/x-dv',
+					'dvi' => 'application/x-dvi',
+					'dxr' => 'application/x-director',
+					'eps' => 'application/postscript',
+					'etx' => 'text/x-setext',
+					'exe' => 'application/octet-stream',
+					'ez' => 'application/andrew-inset',
+					'flv' => 'video/x-flv',
+					'gif' => 'image/gif',
+					'gram' => 'application/srgs',
+					'grxml' => 'application/srgs+xml',
+					'gtar' => 'application/x-gtar',
+					'gz' => 'application/x-gzip',
+					'hdf' => 'application/x-hdf',
+					'hqx' => 'application/mac-binhex40',
+					'htm' => 'text/html',
+					'html' => 'text/html',
+					'ice' => 'x-conference/x-cooltalk',
+					'ico' => 'image/x-icon',
+					'ics' => 'text/calendar',
+					'ief' => 'image/ief',
+					'ifb' => 'text/calendar',
+					'iges' => 'model/iges',
+					'igs' => 'model/iges',
+					'jnlp' => 'application/x-java-jnlp-file',
+					'jp2' => 'image/jp2',
+					'jpe' => 'image/jpeg',
+					'jpeg' => 'image/jpeg',
+					'jpg' => 'image/jpeg',
+					'js' => 'application/x-javascript',
+					'kar' => 'audio/midi',
+					'latex' => 'application/x-latex',
+					'lha' => 'application/octet-stream',
+					'lzh' => 'application/octet-stream',
+					'm3u' => 'audio/x-mpegurl',
+					'm4a' => 'audio/mp4a-latm',
+					'm4p' => 'audio/mp4a-latm',
+					'm4u' => 'video/vnd.mpegurl',
+					'm4v' => 'video/x-m4v',
+					'mac' => 'image/x-macpaint',
+					'man' => 'application/x-troff-man',
+					'mathml' => 'application/mathml+xml',
+					'me' => 'application/x-troff-me',
+					'mesh' => 'model/mesh',
+					'mid' => 'audio/midi',
+					'midi' => 'audio/midi',
+					'mif' => 'application/vnd.mif',
+					'mov' => 'video/quicktime',
+					'movie' => 'video/x-sgi-movie',
+					'mp2' => 'audio/mpeg',
+					'mp3' => 'audio/mpeg',
+					'mp4' => 'video/mp4',
+					'mpe' => 'video/mpeg',
+					'mpeg' => 'video/mpeg',
+					'mpg' => 'video/mpeg',
+					'mpga' => 'audio/mpeg',
+					'ms' => 'application/x-troff-ms',
+					'msh' => 'model/mesh',
+					'mxu' => 'video/vnd.mpegurl',
+					'nc' => 'application/x-netcdf',
+					'oda' => 'application/oda',
+					'ogg' => 'application/ogg',
+					'ogv' => 'video/ogv',
+					'pbm' => 'image/x-portable-bitmap',
+					'pct' => 'image/pict',
+					'pdb' => 'chemical/x-pdb',
+					'pdf' => 'application/pdf',
+					'pgm' => 'image/x-portable-graymap',
+					'pgn' => 'application/x-chess-pgn',
+					'pic' => 'image/pict',
+					'pict' => 'image/pict',
+					'png' => 'image/png',
+					'pnm' => 'image/x-portable-anymap',
+					'pnt' => 'image/x-macpaint',
+					'pntg' => 'image/x-macpaint',
+					'ppm' => 'image/x-portable-pixmap',
+					'ppt' => 'application/vnd.ms-powerpoint',
+					'ps' => 'application/postscript',
+					'qt' => 'video/quicktime',
+					'qti' => 'image/x-quicktime',
+					'qtif' => 'image/x-quicktime',
+					'ra' => 'audio/x-pn-realaudio',
+					'ram' => 'audio/x-pn-realaudio',
+					'ras' => 'image/x-cmu-raster',
+					'rdf' => 'application/rdf+xml',
+					'rgb' => 'image/x-rgb',
+					'rm' => 'application/vnd.rn-realmedia',
+					'roff' => 'application/x-troff',
+					'rtf' => 'text/rtf',
+					'rtx' => 'text/richtext',
+					'sgm' => 'text/sgml',
+					'sgml' => 'text/sgml',
+					'sh' => 'application/x-sh',
+					'shar' => 'application/x-shar',
+					'silo' => 'model/mesh',
+					'sit' => 'application/x-stuffit',
+					'skd' => 'application/x-koan',
+					'skm' => 'application/x-koan',
+					'skp' => 'application/x-koan',
+					'skt' => 'application/x-koan',
+					'smi' => 'application/smil',
+					'smil' => 'application/smil',
+					'snd' => 'audio/basic',
+					'so' => 'application/octet-stream',
+					'spl' => 'application/x-futuresplash',
+					'src' => 'application/x-wais-source',
+					'sv4cpio' => 'application/x-sv4cpio',
+					'sv4crc' => 'application/x-sv4crc',
+					'svg' => 'image/svg+xml',
+					'swf' => 'application/x-shockwave-flash',
+					't' => 'application/x-troff',
+					'tar' => 'application/x-tar',
+					'tcl' => 'application/x-tcl',
+					'tex' => 'application/x-tex',
+					'texi' => 'application/x-texinfo',
+					'texinfo' => 'application/x-texinfo',
+					'tif' => 'image/tiff',
+					'tiff' => 'image/tiff',
+					'tr' => 'application/x-troff',
+					'tsv' => 'text/tab-separated-values',
+					'txt' => 'text/plain',
+					'ustar' => 'application/x-ustar',
+					'vcd' => 'application/x-cdlink',
+					'vrml' => 'model/vrml',
+					'vxml' => 'application/voicexml+xml',
+					'wav' => 'audio/x-wav',
+					'wbmp' => 'image/vnd.wap.wbmp',
+					'wbxml' => 'application/vnd.wap.wbxml',
+					'webm' => 'video/webm',
+					'wml' => 'text/vnd.wap.wml',
+					'wmlc' => 'application/vnd.wap.wmlc',
+					'wmls' => 'text/vnd.wap.wmlscript',
+					'wmlsc' => 'application/vnd.wap.wmlscriptc',
+					'wmv' => 'video/x-ms-wmv',
+					'wrl' => 'model/vrml',
+					'xbm' => 'image/x-xbitmap',
+					'xht' => 'application/xhtml+xml',
+					'xhtml' => 'application/xhtml+xml',
+					'xls' => 'application/vnd.ms-excel',
+					'xml' => 'application/xml',
+					'xpm' => 'image/x-xpixmap',
+					'xsl' => 'application/xml',
+					'xslt' => 'application/xslt+xml',
+					'xul' => 'application/vnd.mozilla.xul+xml',
+					'xwd' => 'image/x-xwindowdump',
+					'xyz' => 'chemical/x-xyz',
+					'zip' => 'application/zip'
+				);
+				$basename = basename($request);
+				$ext = strtolower(substr($basename,strrpos($basename,'.')+1));
+				if(isset($mime_types[$ext])){
+					$this->content_type=$mime_types[$ext];
+				}
+				$direct = false;
+				if(in_array($ext,explode('|',strtolower(DIRECT_EXT)))){
+					$direct = true;
 				}
 			}
 		}
 		
 		//开始处理
-		$this->handle($request);
+		$delete = false;
+		if(count($purge = explode(PURGE_KEY.'/',$request,2))>1){
+			$delete = true;
+			$request = $purge[1];
+		}
+		$key = (NO_KEY)?$request:md5($request).'_'.strlen($request).'.'.$ext;
+		$this->hit = false;
+		$this->handle($request,$key,$delete,$direct);
 		
 	}
 	/**
 	 * 获取内容并输出
 	 * 如果stroage里面不存在,则从URL里面获取
 	 * */
-	private function handle($filename){
+	private function handle($filename,$key,$delete = false,$direct = false){
 		$content = '';
 		if($this->succeed){
-			$storage = new storage();
-			if($storage->exists($filename)){
-				$content = $storage->read($filename);
-			}else{
-				$content = @file_get_contents(BASE_URL.$filename);
-				$storage->write($filename, $content);
+			$storage = storage::gethandle();
+			if($delete){
+				if(!$storage->exists($key)){
+					die(json_encode(array('purge'=>$filename,'key'=>$key,'success'=>'not exists')));
+				}
+				$return = $storage->delete($key);
+				die(json_encode(array('purge'=>$filename,'key'=>$key,'success'=>$return)));
 			}
-			if(empty($content)){
-				$this->error_type = 3;
-				$this->succeed = FALSE;
+			if($storage->exists($key) && !$direct){
+				if(!NO_LOCATE && $url = $storage->url($key)){
+					$this->locate($url);
+				}
+				$content = $storage->read($key);
+				$this->hit = $key;
+				if(empty($content)){
+					$this->succeed = false;
+					$this->error_type = 'empty_conent';
+				}
 			}else{
-				//这里应该有更多的检查
+				//$content = @file_get_contents(BASE_URL.$filename);
+				$content = lib::fetch_url(BASE_URL.$filename);
+				if(!is_array($content) || count($content)<2){
+					$this->succeed = false;
+					$this->error_type = 'fetch_error';
+				}elseif($content[0]==200){
+					//返回200,才写入
+					if(!$direct) $storage->write($key, $content[1]);
+				}else{
+					header('HTTP/1.1 '.$content[0]);
+				}
+				$content = $content[1];
 			}
 		}
 		//显示内容
@@ -86,21 +293,35 @@ class controller{
 	 * 输出结果,包括缓存控制等
 	 * */
 	private function render($content=''){
+		ob_end_clean();
 		if(!$this->succeed){
 			$this->error();
 			return ;
 		}else{
+			if($this->hit){
+				header('Layer-Cache: Hit;key='.$this->hit.';ENV='.RUN_ENV);
+			}else{
+				header('Layer-Cache: Miss;ENV='.RUN_ENV);
+			}
 			header("Expires: " . date("D, j M Y H:i:s GMT", time()+2592000));//缓存一月
 			header('Content-type: '.$this->content_type);
 			echo $content;
 		}
 	}
 	
+	private function locate($url){
+		//302
+		header("HTTP/1.1 302 Moved Temporarily");
+		header("Location:".$url);
+		die();
+	}
+	
 	/**
 	 * 处理错误
 	 * */
 	private function error(){
-		echo "<strong>something seems wrong.</strong>";
+		$this->content_type = 'text/html';
+		echo json_encode(array('error'=>$this->error_type));
 	}
 	
 	

+ 45 - 0
include/lib.php

@@ -17,5 +17,50 @@ class lib{
 
     }
 	
+	//抓取
+	public static function fetch_url($url){
+		switch(RUN_ENV){
+			case 'SAE':	//使用SAE FetchURL服务
+				$f = new SaeFetchurl();
+				if(STATIC_HOST){
+					$f->setHeader('Host',STATIC_HOST);
+				}
+				$content = $f->fetch($url);
+				return array($f->HttpCode(),$content);
+			break;
+			case 'BAE':
+			case 'LOCAL':
+			default:
+				if(function_exists('curl_init')){
+					//BAE或普通平台下可使用curl
+					$ch = curl_init();
+					curl_setopt($ch, CURLOPT_URL, $url);
+					curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+					curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
+					curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
+					if(!ini_get('safe_mode')){
+						curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
+					}
+					if(STATIC_HOST){
+						curl_setopt($ch, CURLOPT_HTTPHEADER, array('Host: '.STATIC_HOST));
+					}
+					$con = curl_exec($ch);
+					$cod = curl_getinfo($ch,CURLINFO_HTTP_CODE);
+					return array($cod,$con);
+				}else{
+					//否则使用file_get_contents
+					$content = '';
+					if(STATIC_HOST){
+						$opt=array('http'=>array('header'=>'Host: '.STATIC_HOST));
+						$context=stream_context_create($opt);
+						$content = file_get_contents($url,false,$context);
+					}else{
+						$content = file_get_contents($url);
+					}
+					list($version,$status_code,$msg) = explode(' ',$http_response_header[0], 3);
+					return array($status_code,$content);
+				}
+		}
+	}
 
 }

+ 8 - 2
include/start.php

@@ -11,5 +11,11 @@ function includeloader($class){
 }
 
 spl_autoload_register('includeloader');
-
-new controller(isset($_GET['q'])?$_GET['q']:'');
+//die($_SERVER['QUERY_STRING']);
+new controller(isset($_SERVER['REQUEST_URI'])?$_SERVER['REQUEST_URI']:'');
+//print_r($_SERVER);die();
+//$url = $_SERVER['DOCUMENT_ROOT'].(isset($_SERVER['REQUEST_URI'])?$_SERVER['REQUEST_URI']:'');
+//$url = str_ireplace('\\','/',$url);
+//$base = str_ireplace('\\','/',BASE_PATH);
+//$test = (explode($base,$url));
+//new controller($test[1]);	//做path_info兼容

+ 6 - 30
include/storage.php

@@ -3,38 +3,14 @@
 if ( ! defined('BASE_PATH')) exit('No direct script access allowed');
 
 /**
- * 封装SAE storage
+ * 封装storage
  * */
 class storage{
-	
-	public $instance;
-	
-	public $domain;
-	
-	public function __construct(){
-		
-		$this->domain = DOMAIN;
-		$this->instance = new SaeStorage(SAE_ACCESSKEY,SAE_SECRETKEY);
-	}
-	
-	public function exists($filename){
-		return $this->instance->fileExists($this->domain,$filename);
-	}
-	//这里是效率瓶颈啊!!
-	public function read($filename){
-		return $this->instance->read($this->domain,$filename);
-	}
-	
-	public function write($name,$content){
-		return $this->instance->write($this->domain,$name,$content);
-	}
-	
-	public function url($name){
-		return $this->instance->getUrl($this->domain,$name);
-	}
-	
-	public function error(){
-		return $this->instance->error();
+	public static function gethandle(){
+		$include_dir = dirname(__FILE__).'/storages/';
+		require($include_dir.ucfirst(strtolower(RUN_ENV.'.php')));
+		return new StorageHandle();
+		//$this->instance = new SaeStorage(SAE_ACCESSKEY,SAE_SECRETKEY);
 	}
 	
 }

+ 45 - 0
include/storages/Bae.php

@@ -0,0 +1,45 @@
+<?php
+
+if ( ! defined('BASE_PATH')) exit('No direct script access allowed');
+
+class StorageHandle{
+	
+	public $instance;
+	
+	public $domain;
+	
+	public function __construct(){
+		$this->domain = DOMAIN;
+		require dirname(__FILE__).'/bcs/bcs.class.php';
+		$this->instance = new BaiduBCS();
+	}
+	
+	public function exists($filename){
+		return $this->instance->is_object_exist($this->domain,$this->get_file($filename));
+	}
+	//这里是效率瓶颈啊!!
+	public function read($filename){
+		return $this->instance->get_object($this->domain,$this->get_file($filename));
+	}
+	
+	public function write($name,$content){
+		return $this->instance->create_object_by_content($this->domain,$this->get_file($name),$content);
+	}
+	
+	public function url($name){
+		//return $this->instance->getUrl($this->domain,$this->get_file($name));
+		return 'http://bcs.duapp.com/'.$this->domain.$this->get_file($name);
+	}
+	
+	public function error(){
+		return false;
+	}
+	
+	public function delete($name){
+		return $this->instance->delete_object($this->domain,$this->get_file($name));
+	}
+	
+	private function get_file($name){
+		return '/'.ltrim($name,'/');
+	}
+}

+ 70 - 0
include/storages/Gcs.php

@@ -0,0 +1,70 @@
+<?php
+
+if ( ! defined('BASE_PATH')) exit('No direct script access allowed');
+
+class StorageHandle{
+	
+	public $instance;
+	
+	public $headurl;
+	
+	public function __construct(){
+		//$this->domain = DOMAIN;
+		require dirname(__FILE__).'/gcs/GrandCloudStorage.php';
+		$this->instance =  new GrandCloudStorage('http://storage.grandcloud.cn');
+		$this->instance->set_key_secret(CS_AK, CS_SK);
+		$this->instance->set_bucket(DOMAIN);
+		$this->headurl = 'http://storage-'.
+			$this->instance->head_bucket(DOMAIN).
+			'.sdcloud.cn';
+		$this->instance->set_host($this->headurl);
+	}
+	
+	public function exists($filename){
+		//GCS木有正常的检测文件是否存在的API囧,那就获取信息吧,失败就不存在
+		try{
+			$this->instance->head_object($this->get_file($filename));
+			return true;
+		}catch(Exception $e){
+			return false;
+		}
+	}
+	public function read($filename){
+		//GCS你是要闹哪样啊摔
+		$temp = tmpfile();
+		$this->instance->get_object($this->get_file($filename), $temp);
+		fseek($temp,0);
+		$contents = "";
+		while (!feof($temp)){
+			$contents .= fread($temp,8192);
+		}
+		return $contents;
+	}
+	
+	public function write($name,$content){
+		//同上啊摔
+		$temp = tmpfile();
+		fwrite($temp,$content);
+		fseek($temp,0);
+		//$temp = tempnam(sys_get_temp_dir());
+		//file_put_contents($temp,$content);
+		$this->instance->put_object($this->get_file($name), $temp);
+		//unlink($temp);
+	}
+	
+	public function url($name){
+		return $this->headurl.$this->instance->get_object_resource($this->get_file($name),30*24*60*60);
+	}
+	
+	public function error(){
+		return false;
+	}
+	
+	public function delete($name){
+		return $this->instance->delete_object($this->get_file($name));
+	}
+	
+	private function get_file($name){
+		return ltrim($name,'/');
+	}
+}

+ 53 - 0
include/storages/Local.php

@@ -0,0 +1,53 @@
+<?php
+
+if ( ! defined('BASE_PATH')) exit('No direct script access allowed');
+
+class StorageHandle{
+	//本地存储
+	var $data_dir;
+	public function __construct(){
+		$data_dir = BASE_PATH.DOMAIN;
+		$data_dir = rtrim($data_dir,'/');
+		$this->data_dir = $data_dir.'/';
+	}
+	public function exists($filename){
+		return is_file($this->get_file($filename));
+	}
+	public function read($filename){
+		return file_get_contents($this->get_file($filename));
+	}
+	public function write($filename,$content){
+		return file_put_contents($this->get_file($filename),$content);
+	}
+	public function url($filename){
+		//return false;	//不提供URL方式读取
+		return rtrim(DOMAIN,'/').'/'.$this->get_file($filename,false);
+	}
+	public function delete($filename){
+		return unlink($this->get_file($filename));
+	}
+	public function error(){
+		return false;
+	}
+	private function get_file($key,$pre = true){
+		if(NO_KEY || NO_SECOND_FLODER){
+			$dir = dirname($this->data_dir.$key);
+			if(!is_dir($dir)){
+				if(!mkdir($dir,0777,true)) die(json_encode(array('error'=>'cannot_make_dir')));
+			}
+			if(!$pre) return $key;
+			return $dir.'/'.$key;
+		}
+		$letter1 = substr($key,0,1);
+		$letter2 = substr($key,0,2);
+		$dir = $this->data_dir.$letter1.'/'.$letter2;
+		if(!is_dir($dir)){
+			if(!mkdir($dir,0777,true)){
+				if(!$pre) return $key;
+				return $this->data_dir.$key;
+			}
+		}
+		if(!$pre) return $letter1.'/'.$letter2.'/'.$key;
+		return $dir.'/'.$key;
+	}
+}

+ 52 - 0
include/storages/Memcached.php

@@ -0,0 +1,52 @@
+<?php
+
+if ( ! defined('BASE_PATH')) exit('No direct script access allowed');
+
+class StorageHandle{
+	
+	public $instance;
+	
+	public function __construct(){
+		//判断环境
+		@include('BaeMemcache.class.php');
+		if(class_exists('BaeMemcache')){
+			$this->instance = new BaeMemcache();
+		}elseif(class_exists('Memcache')){
+			$this->instance = new Memcache();
+			if(method_exists('Memcache','init')){
+				$this->instance->init();
+			}else{
+				$this->instance->connect(defined(CS_AK)?CS_AK:'127.0.0.1',defined(CS_SK)?CS_SK:'11211');
+			}
+		}else{
+			die('No memcache.');
+		}
+	}
+	
+	public function exists($filename){
+		return $this->instance->get($this->get_file($filename));
+	}
+	public function read($filename){
+		return $this->instance->get($this->get_file($filename));
+	}
+	
+	public function write($name,$content){
+		return $this->instance->set($this->get_file($name),$content);
+	}
+	
+	public function url($name){
+		return false;
+	}
+	
+	public function error(){
+		return false;
+	}
+	
+	public function delete($name){
+		return $this->instance->delete($this->get_file($name));
+	}
+	
+	private function get_file($name){
+		return md5($name);
+	}
+}

+ 68 - 0
include/storages/Oss.php

@@ -0,0 +1,68 @@
+<?php
+
+if ( ! defined('BASE_PATH')) exit('No direct script access allowed');
+
+class StorageHandle{
+	
+	public $instance;
+	
+	public $headurl;
+	
+	public $domain;
+	
+	public function __construct(){
+		$this->domain = DOMAIN;
+		require dirname(__FILE__).'/oss/sdk.class.php';
+		$this->instance =  new ALIOSS();
+		$this->headurl = 'http://oss.aliyuncs.com/'.DOMAIN.'/';
+	}
+	
+	public function exists($filename){
+		$res=$this->instance->is_object_exist($this->domain,$this->get_file($filename));
+		if($res->status==404){
+			return false;
+		}
+		return true;
+	}
+	public function read($filename){
+		$bucket = $this->domain;
+		$object = $this->get_file($filename);
+		
+		$options = array(
+			//ALIOSS::OSS_FILE_DOWNLOAD => "d:\\cccccccccc.sh",
+			//ALIOSS::OSS_CONTENT_TYPE => 'txt/html',
+		);	
+		
+		$response = $this->instance->get_object($bucket,$object,$options);
+		return $contents;
+	}
+	
+	public function write($name,$content){
+		$object = $this->get_file($name);
+		$upload_file_options = array(
+			'content' => $content,
+			'length' => strlen($content),
+			ALIOSS::OSS_HEADERS => array(
+				//'Expires' => '2012-10-01 08:00:00',
+			),
+		);
+		
+		$response = $this->instance->upload_file_by_content($this->domain,$object,$upload_file_options);
+	}
+	
+	public function url($name){
+		return $this->headurl.$this->get_file($name);
+	}
+	
+	public function error(){
+		return false;
+	}
+	
+	public function delete($name){
+		return $this->instance->delete_object($this->domain,$this->get_file($name));
+	}
+	
+	private function get_file($name){
+		return ltrim($name,'/');
+	}
+}

+ 43 - 0
include/storages/Sae.php

@@ -0,0 +1,43 @@
+<?php
+
+if ( ! defined('BASE_PATH')) exit('No direct script access allowed');
+
+/**
+ * 封装SAE storage
+ * */
+class StorageHandle{
+	
+	public $instance;
+	
+	public $domain;
+	
+	public function __construct(){
+		$this->domain = DOMAIN;
+		$this->instance = new SaeStorage(SAE_ACCESSKEY,SAE_SECRETKEY);
+	}
+	
+	public function exists($filename){
+		return $this->instance->fileExists($this->domain,$filename);
+	}
+	//这里是效率瓶颈啊!!
+	public function read($filename){
+		return $this->instance->read($this->domain,$filename);
+	}
+	
+	public function write($name,$content){
+		return $this->instance->write($this->domain,$name,$content);
+	}
+	
+	public function url($name){
+		return $this->instance->getUrl($this->domain,$name);
+	}
+	
+	public function error(){
+		return $this->instance->error();
+	}
+	
+	public function delete($name){
+		return $this->instance->delete($this->domain,$name);
+	}
+	
+}

+ 1303 - 0
include/storages/bcs/bcs.class.php

@@ -0,0 +1,1303 @@
+<?php
+if (! defined ( 'BCS_API_PATH' )) {
+	define ( 'BCS_API_PATH', dirname ( __FILE__ ) );
+}
+require_once (BCS_API_PATH . '/conf.inc.php');
+require_once (BCS_API_PATH . '/libs/requestcore/requestcore.class.php');
+require_once (BCS_API_PATH . '/utils/mimetypes.class.php');
+/**
+ * Default BCS Exception.
+ */
+class BCS_Exception extends Exception {
+}
+/**
+ * BCS API
+ */
+class BaiduBCS {
+	/*%******************************************************************************************%*/
+	// CLASS CONSTANTS
+	//百度云存储默认外网域名
+	const DEFAULT_URL = 'bcs.duapp.com';
+	//SDK 版本
+	const API_VERSION = '2012-4-17-1.0.1.6';
+	const ACL = 'acl';
+	const BUCKET = 'bucket';
+	const OBJECT = 'object';
+	const HEADERS = 'headers';
+	const METHOD = 'method';
+	const AK = 'ak';
+	const SK = 'sk';
+	const QUERY_STRING = "query_string";
+	const IMPORT_BCS_LOG_METHOD = "import_bs_log_method";
+	const IMPORT_BCS_PRE_FILTER = "import_bs_pre_filter";
+	const IMPORT_BCS_POST_FILTER = "import_bs_post_filter";
+	/**********************************************************
+	 ******************* Policy Constants**********************
+	 **********************************************************/
+	const STATEMETS = 'statements';
+	//Action 用户动作
+	//'*'代表所有action 
+	const BCS_SDK_ACL_ACTION_ALL = '*';
+	//与bucket相关的action
+	const BCS_SDK_ACL_ACTION_LIST_OBJECT = 'list_object';
+	const BCS_SDK_ACL_ACTION_PUT_BUCKET_POLICY = 'put_bucket_policy';
+	const BCS_SDK_ACL_ACTION_GET_BUCKET_POLICY = 'get_bucket_policy';
+	const BCS_SDK_ACL_ACTION_DELETE_BUCKET = 'delete_bucket';
+	//与object相关的action
+	const BCS_SDK_ACL_ACTION_GET_OBJECT = 'get_object';
+	const BCS_SDK_ACL_ACTION_PUT_OBJECT = 'put_object';
+	const BCS_SDK_ACL_ACTION_DELETE_OBJECT = 'delete_object';
+	const BCS_SDK_ACL_ACTION_PUT_OBJECT_POLICY = 'put_object_policy';
+	const BCS_SDK_ACL_ACTION_GET_OBJECT_POLICY = 'get_object_policy';
+	static $ACL_ACTIONS = array (
+			self::BCS_SDK_ACL_ACTION_ALL, 
+			self::BCS_SDK_ACL_ACTION_LIST_OBJECT, 
+			self::BCS_SDK_ACL_ACTION_PUT_BUCKET_POLICY, 
+			self::BCS_SDK_ACL_ACTION_GET_BUCKET_POLICY, 
+			self::BCS_SDK_ACL_ACTION_DELETE_BUCKET, 
+			self::BCS_SDK_ACL_ACTION_GET_OBJECT, 
+			self::BCS_SDK_ACL_ACTION_PUT_OBJECT, 
+			self::BCS_SDK_ACL_ACTION_DELETE_OBJECT, 
+			self::BCS_SDK_ACL_ACTION_PUT_OBJECT_POLICY, 
+			self::BCS_SDK_ACL_ACTION_GET_OBJECT_POLICY );
+	//EFFECT:
+	const BCS_SDK_ACL_EFFECT_ALLOW = "allow";
+	const BCS_SDK_ACL_EFFECT_DENY = "deny";
+	static $ACL_EFFECTS = array (
+			self::BCS_SDK_ACL_EFFECT_ALLOW, 
+			self::BCS_SDK_ACL_EFFECT_DENY );
+	//ACL_TYPE:
+	//公开读权限
+	const BCS_SDK_ACL_TYPE_PUBLIC_READ = "public-read";
+	//公开写权限(不具备删除权限)
+	const BCS_SDK_ACL_TYPE_PUBLIC_WRITE = "public-write";
+	//公开读写权限(不具备删除权限)
+	const BCS_SDK_ACL_TYPE_PUBLIC_READ_WRITE = "public-read-write";
+	//公开所有权限
+	const BCS_SDK_ACL_TYPE_PUBLIC_CONTROL = "public-control";
+	//私有权限,仅bucket所有者具有所有权限
+	const BCS_SDK_ACL_TYPE_PRIVATE = "private";
+	//SDK中开放此上五种acl_tpe
+	static $ACL_TYPES = array (
+			self::BCS_SDK_ACL_TYPE_PUBLIC_READ, 
+			self::BCS_SDK_ACL_TYPE_PUBLIC_WRITE, 
+			self::BCS_SDK_ACL_TYPE_PUBLIC_READ_WRITE, 
+			self::BCS_SDK_ACL_TYPE_PUBLIC_CONTROL, 
+			self::BCS_SDK_ACL_TYPE_PRIVATE );
+	/*%******************************************************************************************%*/
+	// PROPERTIES
+	//是否使用ssl
+	protected $use_ssl = false;
+	//公钥 account key
+	private $ak;
+	//私钥 secret key
+	private $sk;
+	//云存储server地址
+	private $hostname;
+
+	/**
+	 * 构造函数
+	 * @param string $ak  云存储公钥
+	 * @param string $sk  云存储私钥
+	 * @param string $hostname 云存储Api访问地址
+	 * @throws BCS_Exception
+	 */
+	public function __construct($ak = NULL, $sk = NULL, $hostname = NULL) {
+		//valid ak & sk
+		if (! $ak && ! defined ( 'BCS_AK' ) && false === getenv ( 'HTTP_BAE_ENV_AK' )) {
+			throw new BCS_Exception ( 'No account key was passed into the constructor.' );
+		}
+		if (! $sk && ! defined ( 'BCS_SK' ) && false === getenv ( 'HTTP_BAE_ENV_SK' )) {
+			throw new BCS_Exception ( 'No secret key was passed into the constructor.' );
+		}
+		if ($ak && $sk) {
+			$this->ak = $ak;
+			$this->sk = $sk;
+		} elseif (defined ( 'BCS_AK' ) && defined ( 'BCS_SK' ) && strlen ( BCS_AK ) > 0 && strlen ( BCS_SK ) > 0) {
+			$this->ak = BCS_AK;
+			$this->sk = BCS_SK;
+		} elseif (false !== getenv ( 'HTTP_BAE_ENV_AK' ) && false !== getenv ( 'HTTP_BAE_ENV_SK' )) {
+			$this->ak = getenv ( 'HTTP_BAE_ENV_AK' );
+			$this->sk = getenv ( 'HTTP_BAE_ENV_SK' );
+		} else {
+			throw new BCS_Exception ( 'Construct can not get ak &sk pair, please check!' );
+		}
+		//valid $hostname 
+		if (NULL !== $hostname) {
+			$this->hostname = $hostname;
+		} elseif (false !== getenv ( 'HTTP_BAE_ENV_ADDR_BCS' )) {
+			$this->hostname = getenv ( 'HTTP_BAE_ENV_ADDR_BCS' );
+		} else {
+			$this->hostname = self::DEFAULT_URL;
+		}
+	}
+
+	/**
+	 * 将消息发往Baidu BCS.
+	 * @param array $opt
+	 * @return BCS_ResponseCore
+	 */
+	private function authenticate($opt) {
+		//set common param into opt
+		$opt [self::AK] = $this->ak;
+		$opt [self::SK] = $this->sk;
+		
+		// Validate the S3 bucket name, only list_bucket didnot need validate_bucket
+		if (! ('/' == $opt [self::OBJECT] && '' == $opt [self::BUCKET] && 'GET' == $opt [self::METHOD] && ! isset ( $opt [self::QUERY_STRING] [self::ACL] )) && ! self::validate_bucket ( $opt [self::BUCKET] )) {
+			throw new BCS_Exception ( $opt [self::BUCKET] . 'is not valid, please check!' );
+		}
+		//Validate object
+		if (isset ( $opt [self::OBJECT] ) && ! self::validate_object ( $opt [self::OBJECT] )) {
+			throw new BCS_Exception ( "Invalid object param[" . $opt [self::OBJECT] . "], please check.", - 1 );
+		}
+		//construct url
+		$url = $this->format_url ( $opt );
+		if ($url === false) {
+			throw new BCS_Exception ( 'Can not format url, please check your param!', - 1 );
+		}
+		$opt ['url'] = $url;
+		$this->log ( "[method:" . $opt [self::METHOD] . "][url:$url]", $opt );
+		//build request
+		$request = new BCS_RequestCore ( $opt ['url'] );
+		$headers = array (
+				'Content-Type' => 'application/x-www-form-urlencoded' );
+		
+		$request->set_method ( $opt [self::METHOD] );
+		//Write get_object content to fileWriteTo
+		if (isset ( $opt ['fileWriteTo'] )) {
+			$request->set_write_file ( $opt ['fileWriteTo'] );
+		}
+		// Merge the HTTP headers
+		if (isset ( $opt [self::HEADERS] )) {
+			$headers = array_merge ( $headers, $opt [self::HEADERS] );
+		}
+		// Set content to Http-Body
+		if (isset ( $opt ['content'] )) {
+			$request->set_body ( $opt ['content'] );
+		}
+		// Upload file
+		if (isset ( $opt ['fileUpload'] )) {
+			if (! file_exists ( $opt ['fileUpload'] )) {
+				throw new BCS_Exception ( 'File[' . $opt ['fileUpload'] . '] not found!', - 1 );
+			}
+			$request->set_read_file ( $opt ['fileUpload'] );
+			// Determine the length to read from the file
+			$length = $request->read_stream_size; // The file size by default
+			$file_size = $length;
+			if (isset ( $opt ["length"] )) {
+				if ($opt ["length"] > $file_size) {
+					throw new BCS_Exception ( "Input opt[length] invalid! It can not bigger than file-size", - 1 );
+				}
+				$length = $opt ['length'];
+			}
+			if (isset ( $opt ['seekTo'] ) && ! isset ( $opt ["length"] )) {
+				// Read from seekTo until EOF by default, when set seekTo but not set $opt["length"]
+				$length -= ( integer ) $opt ['seekTo'];
+			}
+			$request->set_read_stream_size ( $length );
+			// Attempt to guess the correct mime-type
+			if ($headers ['Content-Type'] === 'application/x-www-form-urlencoded') {
+				$extension = explode ( '.', $opt ['fileUpload'] );
+				$extension = array_pop ( $extension );
+				$mime_type = BCS_MimeTypes::get_mimetype ( $extension );
+				$headers ['Content-Type'] = $mime_type;
+			}
+			$headers ['Content-MD5'] = '';
+		}
+		// Handle streaming file offsets
+		if (isset ( $opt ['seekTo'] )) {
+			// Pass the seek position to BCS_RequestCore
+			$request->set_seek_position ( ( integer ) $opt ['seekTo'] );
+		}
+		// Add headers to request and compute the string to sign
+		foreach ( $headers as $header_key => $header_value ) {
+			// Strip linebreaks from header values as they're illegal and can allow for security issues
+			$header_value = str_replace ( array (
+					"\r", 
+					"\n" ), '', $header_value );
+			// Add the header if it has a value
+			if ($header_value !== '') {
+				$request->add_header ( $header_key, $header_value );
+			}
+		}
+		// Set the curl options.
+		if (isset ( $opt ['curlopts'] ) && count ( $opt ['curlopts'] )) {
+			$request->set_curlopts ( $opt ['curlopts'] );
+		}
+		$request->send_request ();
+		return new BCS_ResponseCore ( $request->get_response_header (), $request->get_response_body (), $request->get_response_code () );
+	}
+
+	/**
+	 * 获取当前密钥对拥有者的bucket列表
+	 * @param array $opt (Optional) 
+	 * BaiduBCS::IMPORT_BCS_LOG_METHOD - String - Optional: 支持用户传入日志处理函数,函数定义如 function f($log)
+	 * @throws BCS_Exception
+	 * @return BCS_ResponseCore
+	 */
+	public function list_bucket($opt = array()) {
+		$this->assertParameterArray ( $opt );
+		$opt [self::BUCKET] = '';
+		$opt [self::METHOD] = 'GET';
+		$opt [self::OBJECT] = '/';
+		$response = $this->authenticate ( $opt );
+		$this->log ( $response->isOK () ? "List bucket success!" : "List bucket failed! Response: [" . $response->body . "]", $opt );
+		return $response;
+	}
+
+	/**
+	 * 创建 bucket
+	 * @param string $bucket (Required) bucket名称
+	 * @param string $acl (Optional)    bucket权限设置,若为null,使用server分配的默认权限
+	 * @param array $opt (Optional) 
+	 * @throws BCS_Exception
+	 * @return BCS_ResponseCore
+	 */
+	public function create_bucket($bucket, $acl = NULL, $opt = array()) {
+		$this->assertParameterArray ( $opt );
+		$opt [self::BUCKET] = $bucket;
+		$opt [self::METHOD] = 'PUT';
+		$opt [self::OBJECT] = '/';
+		if (NULL !== $acl) {
+			if (! in_array ( $acl, self::$ACL_TYPES )) {
+				throw new BCS_Exception ( "Invalid acl_type[" . $acl . "], please check!", - 1 );
+			}
+			self::set_header_into_opt ( "x-bs-acl", $acl, $opt );
+		}
+		$response = $this->authenticate ( $opt );
+		$this->log ( $response->isOK () ? "Create bucket success!" : "Create bucket failed! Response: [" . $response->body . "]", $opt );
+		return $response;
+	}
+
+	/**
+	 * 删除bucket
+	 * @param string $bucket (Required)
+	 * @param array $opt (Optional)
+	 * @return boolean|BCS_ResponseCore
+	 */
+	public function delete_bucket($bucket, $opt = array()) {
+		$this->assertParameterArray ( $opt );
+		$opt [self::BUCKET] = $bucket;
+		$opt [self::METHOD] = 'DELETE';
+		$opt [self::OBJECT] = '/';
+		$response = $this->authenticate ( $opt );
+		$this->log ( $response->isOK () ? "Delete bucket success!" : "Delete bucket failed! Response: [" . $response->body . "]", $opt );
+		return $response;
+	}
+
+	/**
+	 * 设置bucket的acl,有三种模式,
+	 * (1).设置详细json格式的acl;
+	 * a. $acl 为json的array
+	 * b. $acl 为json的string
+	 * (2).通过acl_type字段进行设置
+	 * a. $acl 为BaiduBCS::$ACL_TYPES中的字段
+	 * @param string $bucket (Required)
+	 * @param string $acl (Required)
+	 * @param array $opt (Optional)
+	 * @return boolean|BCS_ResponseCore
+	 */
+	public function set_bucket_acl($bucket, $acl, $opt = array()) {
+		$this->assertParameterArray ( $opt );
+		$result = $this->analyze_user_acl ( $acl );
+		$opt = array_merge ( $opt, $result );
+		$opt [self::BUCKET] = $bucket;
+		$opt [self::METHOD] = 'PUT';
+		$opt [self::OBJECT] = '/';
+		$opt [self::QUERY_STRING] = array (
+				self::ACL => 1 );
+		$response = $this->authenticate ( $opt );
+		$this->log ( $response->isOK () ? "Set bucket acl success!" : "Set bucket acl failed! Response: [" . $response->body . "]", $opt );
+		return $response;
+	}
+
+	/**
+	 * 获取bucket的acl
+	 * @param string $bucket (Required)
+	 * @param array $opt (Optional)
+	 * @return BCS_ResponseCore
+	 */
+	public function get_bucket_acl($bucket, $opt = array()) {
+		$this->assertParameterArray ( $opt );
+		$opt [self::BUCKET] = $bucket;
+		$opt [self::METHOD] = 'GET';
+		$opt [self::OBJECT] = '/';
+		$opt [self::QUERY_STRING] = array (
+				self::ACL => 1 );
+		$response = $this->authenticate ( $opt );
+		$this->log ( $response->isOK () ? "Get bucket acl success!" : "Get bucket acl failed! Response: [" . $response->body . "]", $opt );
+		return $response;
+	}
+
+	/**
+	 * 获取bucket中object列表
+	 * @param string $bucket (Required)
+	 * @param array $opt (Optional)
+	 * start : 主要用于翻页功能,用法同mysql中start的用法
+	 * limit : 主要用于翻页功能,用法同mysql中limit的用法
+	 * prefix: 只返回以prefix为前缀的object,此处prefix必须以'/'开头
+	 * @throws BCS_Exception
+	 * @return BCS_ResponseCore
+	 */
+	public function list_object($bucket, $opt = array()) {
+		$this->assertParameterArray ( $opt );
+		$opt [self::BUCKET] = $bucket;
+		if (empty ( $opt [self::BUCKET] )) {
+			throw new BCS_Exception ( "Bucket should not be empty, please check", - 1 );
+		}
+		$opt [self::METHOD] = 'GET';
+		$opt [self::OBJECT] = '/';
+		$opt [self::QUERY_STRING] = array ();
+		if (isset ( $opt ['start'] ) && is_int ( $opt ['start'] )) {
+			$opt [self::QUERY_STRING] ['start'] = $opt ['start'];
+		}
+		if (isset ( $opt ['limit'] ) && is_int ( $opt ['limit'] )) {
+			$opt [self::QUERY_STRING] ['limit'] = $opt ['limit'];
+		}
+		if (isset ( $opt ['prefix'] )) {
+			$opt [self::QUERY_STRING] ['prefix'] = rawurlencode ( $opt ['prefix'] );
+		}
+		$response = $this->authenticate ( $opt );
+		$this->log ( $response->isOK () ? "List object success!" : "Lit object failed! Response: [" . $response->body . "]", $opt );
+		return $response;
+	}
+
+	/**
+	 * 以目录形式获取bucket中object列表
+	 * @param string $bucket (Required)
+	 * @param $dir (Required)
+	 * 目录名,格式为必须以'/'开头和结尾,默认为'/'
+	 * @param string $list_model (Required)
+	 * 目录展现形式,值可以为0,1,2,默认为2,以下对各个值的功能进行介绍:
+	 * 0->只返回object列表,不返回子目录列表
+	 * 1->只返回子目录列表,不返回object列表 
+	 * 2->同时返回子目录列表和object列表
+	 * @param array $opt (Optional)
+	 * start : 主要用于翻页功能,用法同mysql中start的用法
+	 * limit : 主要用于翻页功能,用法同mysql中limit的用法
+	 * @throws BCS_Exception
+	 * @return BCS_ResponseCore
+	 */
+	public function list_object_by_dir($bucket, $dir = '/', $list_model = 2, $opt = array()) {
+		$this->assertParameterArray ( $opt );
+		$opt [self::BUCKET] = $bucket;
+		if (empty ( $opt [self::BUCKET] )) {
+			throw new BCS_Exception ( "Bucket should not be empty, please check", - 1 );
+		}
+		$opt [self::METHOD] = 'GET';
+		$opt [self::OBJECT] = '/';
+		$opt [self::QUERY_STRING] = array ();
+		if (isset ( $opt ['start'] ) && is_int ( $opt ['start'] )) {
+			$opt [self::QUERY_STRING] ['start'] = $opt ['start'];
+		}
+		if (isset ( $opt ['limit'] ) && is_int ( $opt ['limit'] )) {
+			$opt [self::QUERY_STRING] ['limit'] = $opt ['limit'];
+		}
+		
+		$opt [self::QUERY_STRING] ['prefix'] = rawurlencode ( $dir );
+		$opt [self::QUERY_STRING] ['dir'] = $list_model;
+		
+		$response = $this->authenticate ( $opt );
+		$this->log ( $response->isOK () ? "List object success!" : "Lit object failed! Response: [" . $response->body . "]", $opt );
+		return $response;
+	}
+
+	/**
+	 * 上传文件
+	 * @param string $bucket (Required) 
+	 * @param string $object (Required) 
+	 * @param string $file (Required); 需要上传的文件的文件路径
+	 * @param array $opt (Optional) 
+	 * filename - Optional; 指定文件名
+	 * acl - Optional ; 上传文件的acl,只能使用acl_type
+	 * seekTo - Optional; 上传文件的偏移位置
+	 * length - Optional; 待上传长度
+	 * @return BCS_ResponseCore
+	 */
+	public function create_object($bucket, $object, $file, $opt = array()) {
+		$this->assertParameterArray ( $opt );
+		$opt [self::BUCKET] = $bucket;
+		$opt [self::OBJECT] = $object;
+		$opt ['fileUpload'] = $file;
+		$opt [self::METHOD] = 'PUT';
+		if (isset ( $opt ['acl'] )) {
+			if (in_array ( $opt ['acl'], self::$ACL_TYPES )) {
+				self::set_header_into_opt ( "x-bs-acl", $opt ['acl'], $opt );
+			} else {
+				throw new BCS_Exception ( "Invalid acl string, it should be acl_type", - 1 );
+			}
+			unset ( $opt ['acl'] );
+		}
+		if (isset ( $opt ['filename'] )) {
+			self::set_header_into_opt ( "Content-Disposition", 'attachment; filename=' . $opt ['filename'], $opt );
+		}
+		$response = $this->authenticate ( $opt );
+		$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 );
+		return $response;
+	}
+
+	/**
+	 * 上传文件
+	 * @param string $bucket (Required) 
+	 * @param string $object (Required) 
+	 * @param string $file (Required); 需要上传的文件的文件路径
+	 * @param array $opt (Optional) 
+	 * filename - Optional; 指定文件名
+	 * acl - Optional ; 上传文件的acl,只能使用acl_type
+	 * @return BCS_ResponseCore
+	 */
+	public function create_object_by_content($bucket, $object, $content, $opt = array()) {
+		$this->assertParameterArray ( $opt );
+		$opt [self::BUCKET] = $bucket;
+		$opt [self::OBJECT] = $object;
+		$opt [self::METHOD] = 'PUT';
+		if ($content !== NULL && is_string ( $content )) {
+			$opt ['content'] = $content;
+		} else {
+			throw new BCS_Exception ( "Invalid object content, please check.", - 1 );
+		}
+		if (isset ( $opt ['acl'] )) {
+			if (in_array ( $opt ['acl'], self::$ACL_TYPES )) {
+				self::set_header_into_opt ( "x-bs-acl", $opt ['acl'], $opt );
+			} else {
+				throw new BCS_Exception ( "Invalid acl string, it should be acl_type", - 1 );
+			}
+			unset ( $opt ['acl'] );
+		}
+		if (isset ( $opt ['filename'] )) {
+			self::set_header_into_opt ( "Content-Disposition", 'attachment; filename=' . $opt ['filename'], $opt );
+		}
+		$response = $this->authenticate ( $opt );
+		$this->log ( $response->isOK () ? "Create object[$object] success!" : "Create object[$object] failed! Response: [" . $response->body . "] Logid[" . $response->header ["x-bs-request-id"] . "]", $opt );
+		return $response;
+	}
+
+	/**
+	 * 通过superfile的方式上传文件
+	 * @param string $bucket (Required) 
+	 * @param string $object (Required) 
+	 * @param string $file (Required); 需要上传的文件的文件路径
+	 * @param array $opt (Optional) 
+	 * filename - Optional; 指定文件名
+	 * sub_object_size - Optional; 指定子文件的划分大小,单位B,建议以256KB为单位进行子object划分,默认为1MB进行划分
+	 * @return BCS_ResponseCore
+	 */
+	public function create_object_superfile($bucket, $object, $file, $opt = array()) {
+		if (isset ( $opt ['length'] ) || isset ( $opt ['seekTo'] )) {
+			throw new BCS_Exception ( "Temporary unsupport opt of length and seekTo of superfile.", - 1 );
+		}
+		//$opt array
+		$this->assertParameterArray ( $opt );
+		$opt [self::BUCKET] = $bucket;
+		$opt ['fileUpload'] = $file;
+		$opt [self::METHOD] = 'PUT';
+		if (isset ( $opt ['acl'] )) {
+			if (in_array ( $opt ['acl'], self::$ACL_TYPES )) {
+				self::set_header_into_opt ( "x-bs-acl", $opt ['acl'], $opt );
+			} else {
+				throw new BCS_Exception ( "Invalid acl string, it should be acl_type", - 1 );
+			}
+			unset ( $opt ['acl'] );
+		}
+		//切片上传
+		if (! file_exists ( $opt ['fileUpload'] )) {
+			throw new BCS_Exception ( 'File not found!', - 1 );
+		}
+		$fileSize = filesize ( $opt ['fileUpload'] );
+		$sub_object_size = 1024 * 1024; //default 1MB
+		if (defined ( "BCS_SUPERFILE_SLICE_SIZE" )) {
+			$sub_object_size = BCS_SUPERFILE_SLICE_SIZE;
+		}
+		if (isset ( $opt ["sub_object_size"] )) {
+			if (is_int ( $opt ["sub_object_size"] ) && $opt ["sub_object_size"] > 0) {
+				$sub_object_size = $opt ["sub_object_size"];
+			} else {
+				throw new BCS_Exception ( "Param [sub_object_size] invalid ,please check!", - 1 );
+			}
+		}
+		$sliceNum = intval ( ceil ( $fileSize / $sub_object_size ) );
+		$this->log ( "File[" . $opt ['fileUpload'] . "], size=[$fileSize], sub_object_size=[$sub_object_size], sub_object_num=[$sliceNum]", $opt );
+		$object_list = array (
+				'object_list' => array () );
+		for($i = 0; $i < $sliceNum; $i ++) {
+			//send slice
+			$opt ['seekTo'] = $i * $sub_object_size;
+			
+			if (($i + 1) === $sliceNum) {
+				//last sub object
+				$opt ['length'] = (0 === $fileSize % $sub_object_size) ? $sub_object_size : $fileSize % $sub_object_size;
+			} else {
+				$opt ['length'] = $sub_object_size;
+			}
+			$opt [self::OBJECT] = $object . BCS_SUPERFILE_POSTFIX . $i;
+			$object_list ['object_list'] ['part_' . $i] = array ();
+			$object_list ['object_list'] ['part_' . $i] ['url'] = 'bs://' . $bucket . $opt [self::OBJECT];
+			$this->log ( "Begin to upload Sub-object[" . $opt [self::OBJECT] . "][$i/$sliceNum][seekto:" . $opt ['seekTo'] . "][Length:" . $opt ['length'] . "]", $opt );
+			$response = $this->create_object ( $bucket, $opt [self::OBJECT], $file, $opt );
+			if ($response->isOK ()) {
+				$this->log ( "Sub-object upload[" . $opt [self::OBJECT] . "][$i/$sliceNum][seekto:" . $opt ['seekTo'] . "][Length:" . $opt ['length'] . "]success! ", $opt );
+				$object_list ['object_list'] ['part_' . $i] ['etag'] = $response->header ['Content-MD5'];
+				continue;
+			} else {
+				$this->log ( "Sub-object upload[" . $opt [self::OBJECT] . "][$i/$sliceNum] failed! ", $opt );
+				return $response;
+			}
+		}
+		//将子文件分片的列表构造成 superfile
+		unset ( $opt ['fileUpload'] );
+		unset ( $opt ['length'] );
+		unset ( $opt ['seekTo'] );
+		$opt ['content'] = self::array_to_json ( $object_list );
+		$opt [self::QUERY_STRING] = array (
+				"superfile" => 1 );
+		$opt [self::OBJECT] = $object;
+		if (isset ( $opt ['filename'] )) {
+			self::set_header_into_opt ( "Content-Disposition", 'attachment; filename=' . $opt ['filename'], $opt );
+		}
+		$response = $this->authenticate ( $opt );
+		$this->log ( $response->isOK () ? "Create object-superfile success!" : "Create object-superfile failed! Response: [" . $response->body . "]", $opt );
+		return $response;
+	}
+
+	/**
+	 * 将目录中的所有文件进行上传,每个文件为单独object,object命名方式下详:
+	 * 如有 /home/worker/a/b/c.txt  需上传目录为$dir=/home/worker/a  
+	 * object命令方式为
+	 * 1. object默认命名方式为 “子目录名 +文件名”,如上述文件c.txt,默认为 '/b/c.txt'
+	 * 2. 增强命名模式,在$opt中有可选参数进行配置
+	 * 举例说明 :prefix . has_sub_directory?"/b":"" . '/c.txt' 
+	 * @param string $bucket (Required) 
+	 * @param string $dir (Required) 
+	 * @param array $opt(Optional) 
+	 * string prefix 文件object前缀
+	 * boolean has_sub_directory(default=true)   object命名中是否携带文件的子目录结构,若置为false,请确认待上传的目录和所有子目录中没有重名文件,否则会产生object覆盖问题
+	 * BaiduBCS::IMPORT_BCS_PRE_FILTER   用户可自定义上传文件前的操作函数
+	 * 1. 函数参数列表顺序需为 ($bucket,$object,$file,&$opt),注意$opt为upload_directory函数传入的$opt的拷贝,只对当前object生效
+	 * 2. 函数返回值必须为boolean,当true该文件进行上传,若false跳过上传
+	 * 3. 如果函数返回false,将不会进行post_filter的调用
+	 * BaiduBCS::IMPORT_BCS_POST_FILTER  用户可自定义上传文件后的操作函数
+	 * 1. 函数参数列表顺序需为 ($bucket,$object,$file,&$opt,$response),注意$opt为upload_directory函数传入的$opt的拷贝,只对当前object生效
+	 * 2. 函数返回值无要求
+	 * string seek_object 用户断点续传,需要为object名称,如果该object在目录中不存在,抛出异常,若存在则将该object和此后的object进行上传
+	 * string seek_object_id 作用同seek_object,只需要传入上传过程中日志中展示的[a/b]中object序号即可,注意object序号是以1开始计算的
+	 * @return array  数组形式的上传结果
+	 * 'success' => int  上传成功的文件数目
+	 * 'skipped' => int  被跳过的文件
+	 * 'failed' => array()   上传失败的文件
+	 * 
+	 */
+	public function upload_directory($bucket, $dir, $opt = array()) {
+		$this->assertParameterArray ( $opt );
+		if (! is_dir ( $dir )) {
+			throw new BCS_Exception ( "$dir is not a dir!", - 1 );
+		}
+		$result = array (
+				"success" => 0, 
+				"failed" => array (), 
+				"skipped" => 0 );
+		$prefix = "";
+		if (isset ( $opt ['prefix'] )) {
+			$prefix = $opt ['prefix'];
+		}
+		$has_sub_directory = true;
+		if (isset ( $opt ['has_sub_directory'] ) && is_bool ( $opt ['has_sub_directory'] )) {
+			$has_sub_directory = $opt ['has_sub_directory'];
+		}
+		//获取文件树和构造object名
+		$file_tree = self::get_filetree ( $dir );
+		$objects = array ();
+		foreach ( $file_tree as $file ) {
+			$object = $has_sub_directory == true ? substr ( $file, strlen ( $dir ) ) : "/" . basename ( $file );
+			$objects [$prefix . $object] = $file;
+		}
+		$objectCount = count ( $objects );
+		$before_upload_log = "Upload directory: bucket[$bucket] upload_dir[$dir] file_sum[$objectCount]";
+		if (isset ( $opt ["seek_object_id"] )) {
+			$before_upload_log .= " seek_object_id[" . $opt ["seek_object_id"] . "/$objectCount]";
+		}
+		if (isset ( $opt ["seek_object"] )) {
+			$before_upload_log .= " seek_object[" . $opt ["seek_object"] . "]";
+		}
+		$this->log ( $before_upload_log, $opt );
+		//查看是否需要查询断点,进行断点续传
+		if (isset ( $opt ["seek_object_id"] ) && isset ( $opt ["seek_object"] )) {
+			throw new BCS_Exception ( "Can not set see_object_id and seek_object at the same time!", - 1 );
+		}
+		
+		$num = 1;
+		if (isset ( $opt ["seek_object"] )) {
+			if (isset ( $objects [$opt ["seek_object"]] )) {
+				foreach ( $objects as $object => $file ) {
+					if ($object != $opt ["seek_object"]) {
+						//当非断点文件,该object已完成上传
+						$this->log ( "Seeking[" . $opt ["seek_object"] . "]. Skip id[$num/$objectCount]object[$object]file[$file].", $opt );
+						//$result ['skipped'] [] = "[$num/$objectCount]  " . $file;
+						$result ['skipped'] ++;
+						unset ( $objects [$object] );
+					} else {
+						//当找到断点文件,停止循环,从断点文件重新上传
+						//当非断点文件,该object已完成上传
+						$this->log ( "Found seek id[$num/$objectCount]object[$object]file[$file], begin from here.", $opt );
+						break;
+					}
+					$num ++;
+				}
+			} else {
+				throw new BCS_Exception ( "Can not find you seek object, please check!", - 1 );
+			}
+		}
+		if (isset ( $opt ["seek_object_id"] )) {
+			if (is_int ( $opt ["seek_object_id"] ) && $opt ["seek_object_id"] <= $objectCount) {
+				foreach ( $objects as $object => $file ) {
+					if ($num < $opt ["seek_object_id"]) {
+						$this->log ( "Seeking object of [" . $opt ["seek_object_id"] . "/$objectCount]. Skip  id[$num/$objectCount]object[$object]file[$file].", $opt );
+						//$result ['skipped'] [] = "[$num/$objectCount]  " . $file;
+						$result ['skipped'] ++;
+						unset ( $objects [$object] );
+					} else {
+						break;
+					}
+					$num ++;
+				}
+			} else {
+				throw new BCS_Exception ( "Param seek_object_id not valid, please check!", - 1 );
+			}
+		}
+		//上传objects
+		$objectCount = count ( $objects );
+		foreach ( $objects as $object => $file ) {
+			$tmp_opt = array_merge ( $opt );
+			if (isset ( $opt [self::IMPORT_BCS_PRE_FILTER] ) && function_exists ( $opt [self::IMPORT_BCS_PRE_FILTER] )) {
+				$bolRes = $opt [self::IMPORT_BCS_PRE_FILTER] ( $bucket, $object, $file, $tmp_opt );
+				if ($bolRes !== true) {
+					$this->log ( "User pre_filter_function return un-true. Skip id[$num/$objectCount]object[$object]file[$file].", $opt );
+					//$result ['skipped'] [] = "id[$num/$objectCount]object[$object]file[$file]";
+					$result ['skipped'] ++;
+					$num ++;
+					continue;
+				}
+			}
+			try {
+				$response = $this->create_object ( $bucket, $object, $file, $tmp_opt );
+			} catch ( Exception $e ) {
+				$this->log ( $e->getMessage (), $opt );
+				$this->log ( "Upload Failed id[$num/$objectCount]object[$object]file[$file].", $opt );
+				$num ++;
+				continue;
+			}
+			if ($response->isOK ()) {
+				$result ["success"] ++;
+				$this->log ( "Upload Success id[$num/$objectCount]object[$object]file[$file].", $opt );
+			} else {
+				$result ["failed"] [] = "id[$num/$objectCount]object[$object]file[$file]";
+				$this->log ( "Upload Failed id[$num/$objectCount]object[$object]file[$file].", $opt );
+			}
+			if (isset ( $opt [self::IMPORT_BCS_POST_FILTER] ) && function_exists ( $opt [self::IMPORT_BCS_POST_FILTER] )) {
+				$opt [self::IMPORT_BCS_POST_FILTER] ( $bucket, $object, $file, $tmp_opt, $response );
+			}
+			$num ++;
+		}
+		//打印日志并返回结果数组
+		$result_str = "\r\n\r\nUpload $dir to $bucket finished!\r\n";
+		$result_str .= "**********************************************************\r\n";
+		$result_str .= "**********************Result Summary**********************\r\n";
+		$result_str .= "**********************************************************\r\n";
+		$result_str .= "Upload directory :  [$dir]\r\n";
+		$result_str .= "File num :  [$objectCount]\r\n";
+		$result_str .= "Success: \r\n\tNum: " . $result ["success"] . "\r\n";
+		$result_str .= "Skipped:\r\n\tNum:" . $result ["skipped"] . "\r\n";
+		//		foreach ( $result ["skipped"] as $skip ) {
+		//			$result_str .= "\t$skip\r\n";
+		//		}
+		$result_str .= "Failed:\r\n\tNum:" . count ( $result ["failed"] ) . "\r\n";
+		foreach ( $result ["failed"] as $fail ) {
+			$result_str .= "\t$fail\r\n";
+		}
+		if (isset ( $opt [self::IMPORT_BCS_LOG_METHOD] )) {
+			$this->log ( $result_str, $opt );
+		} else {
+			echo $result_str;
+		}
+		return $result;
+	}
+
+	/**
+	 * 通过此方法以拷贝的方式创建object,object来源为$source
+	 * @param array $source (Required)  object 来源
+	 * bucket(Required)
+	 * object(Required)
+	 * @param array $dest (Required)    待拷贝的目标object
+	 * bucket(Required)
+	 * object(Required)
+	 * @param array $opt (Optional) 
+	 * source_tag 指定拷贝对象的版本号
+	 * @throws BCS_Exception
+	 * @return BCS_ResponseCore
+	 */
+	public function copy_object($source, $dest, $opt = array()) {
+		$this->assertParameterArray ( $opt );
+		//valid source and dest
+		if (empty ( $source ) || ! is_array ( $source ) || ! isset ( $source [self::BUCKET] ) || ! isset ( $source [self::OBJECT] )) {
+			throw new BCS_Exception ( '$source invalid, please check!', - 1 );
+		}
+		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] )) {
+			throw new BCS_Exception ( '$dest invalid, please check!', - 1 );
+		}
+		$opt [self::BUCKET] = $dest [self::BUCKET];
+		$opt [self::OBJECT] = $dest [self::OBJECT];
+		$opt [self::METHOD] = 'PUT';
+		self::set_header_into_opt ( 'x-bs-copy-source', 'bs://' . $source [self::BUCKET] . $source [self::OBJECT], $opt );
+		if (isset ( $opt ['source_tag'] )) {
+			self::set_header_into_opt ( 'x-bs-copy-source-tag', $opt ['source_tag'], $opt );
+		}
+		$response = $this->authenticate ( $opt );
+		$this->log ( $response->isOK () ? "Copy object success!" : "Copy object failed! Response: [" . $response->body . "]", $opt );
+		return $response;
+	}
+
+	/**
+	 * 设置object的meta信息
+	 * @param string $bucket (Required) 
+	 * @param string $object (Required) 
+	 * @param array $opt (Optional) 
+	 * 目前支持的meta信息如下:
+	 * Content-Type
+	 * Cache-Control
+	 * Content-Disposition
+	 * Content-Encoding
+	 * Content-MD5
+	 * Expires
+	 * @return BCS_ResponseCore
+	 */
+	public function set_object_meta($bucket, $object, $meta, $opt = array()) {
+		$this->assertParameterArray ( $opt );
+		$this->assertParameterArray ( $meta );
+		$opt [self::BUCKET] = $bucket;
+		$opt [self::OBJECT] = $object;
+		$opt [self::METHOD] = 'PUT';
+		//利用copy_object接口来设置meta信息
+		$source = "bs://$bucket$object";
+		if (empty ( $meta )) {
+			throw new BCS_Exception ( '$meta can not be empty! And $meta must be array.', - 1 );
+		}
+		foreach ( $meta as $header => $value ) {
+			self::set_header_into_opt ( $header, $value, $opt );
+		}
+		$source = array (
+				self::BUCKET => $bucket, 
+				self::OBJECT => $object );
+		$response = $this->copy_object ( $source, $source, $opt );
+		$this->log ( $response->isOK () ? "Set object meta success!" : "Set object meta failed! Response: [" . $response->body . "]", $opt );
+		return $response;
+	}
+
+	/**
+	 * 获取object的acl
+	 * @param string $bucket (Required) 
+	 * @param string $object (Required) 
+	 * @param array $opt (Optional) 
+	 * @throws BCS_Exception
+	 * @return BCS_ResponseCore
+	 */
+	public function get_object_acl($bucket, $object, $opt = array()) {
+		$this->assertParameterArray ( $opt );
+		$opt [self::BUCKET] = $bucket;
+		$opt [self::METHOD] = 'GET';
+		$opt [self::OBJECT] = $object;
+		$opt [self::QUERY_STRING] = array (
+				self::ACL => 1 );
+		$response = $this->authenticate ( $opt );
+		$this->log ( $response->isOK () ? "Get object acl success!" : "Get object acl failed! Response: [" . $response->body . "]", $opt );
+		return $response;
+	}
+
+	/**
+	 * 设置object的acl,有三种模式,
+	 * (1).设置详细json格式的acl;
+	 * a. $acl 为json的array
+	 * b. $acl 为json的string
+	 * (2).通过acl_type字段进行设置
+	 * a. $acl 为BaiduBCS::$ACL_ACTIONS中的字段
+	 * @param string $bucket (Required)
+	 * @param string $object (Required)
+	 * @param string|array $acl (Required)
+	 * @param array $opt (Optional)
+	 * @return BCS_ResponseCore
+	 */
+	public function set_object_acl($bucket, $object, $acl, $opt = array()) {
+		$this->assertParameterArray ( $opt );
+		//analyze acl
+		$result = $this->analyze_user_acl ( $acl );
+		$opt = array_merge ( $opt, $result );
+		$opt [self::BUCKET] = $bucket;
+		$opt [self::METHOD] = 'PUT';
+		$opt [self::OBJECT] = $object;
+		$opt [self::QUERY_STRING] = array (
+				self::ACL => 1 );
+		$response = $this->authenticate ( $opt );
+		$this->log ( $response->isOK () ? "Set object acl success!" : "Set object acl failed! Response: [" . $response->body . "]", $opt );
+		return $response;
+	}
+
+	/**
+	 * 删除object
+	 * @param string $bucket (Required)
+	 * @param string $object (Required)
+	 * @param array $opt (Optional)
+	 * @throws BCS_Exception
+	 * @return BCS_ResponseCore
+	 */
+	public function delete_object($bucket, $object, $opt = array()) {
+		$this->assertParameterArray ( $opt );
+		$opt [self::BUCKET] = $bucket;
+		$opt [self::METHOD] = 'DELETE';
+		$opt [self::OBJECT] = $object;
+		$response = $this->authenticate ( $opt );
+		$this->log ( $response->isOK () ? "Delete object success!" : "Delete object failed! Response: [" . $response->body . "]", $opt );
+		return $response;
+	}
+
+	/**
+	 * 判断object是否存在
+	 * @param string $bucket (Required)
+	 * @param string $object (Required)
+	 * @param array $opt (Optional)
+	 * @throws BCS_Exception
+	 * @return boolean true|boolean false|BCS_ResponseCore
+	 * true:object存在
+	 * false:不存在
+	 * BCS_ResponseCore其他错误
+	 */
+	public function is_object_exist($bucket, $object, $opt = array()) {
+		$this->assertParameterArray ( $opt );
+		$opt [self::BUCKET] = $bucket;
+		$opt [self::METHOD] = 'HEAD';
+		$opt [self::OBJECT] = $object;
+		$response = $this->get_object_info ( $bucket, $object, $opt );
+		if ($response->isOK ()) {
+			return true;
+		} elseif ($response->status === 404) {
+			return false;
+		}
+		return $response;
+	}
+
+	/**
+	 * 获取文件信息,发送的为HTTP HEAD请求,文件信息都在http response的header中,不会提取文件的内容
+	 * @param string $bucket (Required)
+	 * @param string $object (Required)
+	 * @param array $opt (Optional)
+	 * @throws BCS_Exception
+	 * @return array BCS_ResponseCore
+	 */
+	public function get_object_info($bucket, $object, $opt = array()) {
+		$this->assertParameterArray ( $opt );
+		$opt [self::BUCKET] = $bucket;
+		$opt [self::METHOD] = 'HEAD';
+		$opt [self::OBJECT] = $object;
+		$response = $this->authenticate ( $opt );
+		$this->log ( $response->isOK () ? "Get object info success!" : "Get object info failed! Response: [" . $response->body . "]", $opt );
+		return $response;
+	}
+
+	/**
+	 * 下载object
+	 * @param string $bucket (Required)
+	 * @param string $object (Required)
+	 * @param array $opt (Optional)
+	 * fileWriteTo   (Optional)直接将请求结果写入该文件,如果fileWriteTo文件存在,sdk进行重命名再存储
+	 * @throws BCS_Exception
+	 * @return BCS_ResponseCore
+	 */
+	public function get_object($bucket, $object, $opt = array()) {
+		$this->assertParameterArray ( $opt );
+		//若fileWriteTo待写入的文件已经存在,需要进行重命名
+		if (isset ( $opt ["fileWriteTo"] ) && file_exists ( $opt ["fileWriteTo"] )) {
+			$original_file_write_to = $opt ["fileWriteTo"];
+			$arr = explode ( DIRECTORY_SEPARATOR, $opt ["fileWriteTo"] );
+			$file_name = $arr [count ( $arr ) - 1];
+			$num = 1;
+			while ( file_exists ( $opt ["fileWriteTo"] ) ) {
+				$new_name_arr = explode ( ".", $file_name );
+				if (count ( $new_name_arr ) > 1) {
+					$new_name_arr [count ( $new_name_arr ) - 2] .= " ($num)";
+				} else {
+					$new_name_arr [0] .= " ($num)";
+				}
+				$arr [count ( $arr ) - 1] = implode ( ".", $new_name_arr );
+				$opt ["fileWriteTo"] = implode ( DIRECTORY_SEPARATOR, $arr );
+				$num ++;
+			}
+			$this->log ( "[$original_file_write_to] already exist, rename it to [" . $opt ["fileWriteTo"] . "]", $opt );
+		}
+		$opt [self::BUCKET] = $bucket;
+		$opt [self::METHOD] = 'GET';
+		$opt [self::OBJECT] = $object;
+		$response = $this->authenticate ( $opt );
+		$this->log ( $response->isOK () ? "Get object success!" : "Get object failed! Response: [" . $response->body . "]", $opt );
+		if (! $response->isOK () && isset ( $opt ["fileWriteTo"] )) {
+			unlink ( $opt ["fileWriteTo"] );
+		}
+		return $response;
+	}
+
+	/**
+	 * 生成签名链接
+	 */
+	private function generate_user_url($method, $bucket, $object, $opt = array()) {
+		$opt [self::AK] = $this->ak;
+		$opt [self::SK] = $this->sk;
+		$opt [self::BUCKET] = $bucket;
+		$opt [self::METHOD] = $method;
+		$opt [self::OBJECT] = $object;
+		$opt [self::QUERY_STRING] = array ();
+		if (isset ( $opt ["time"] )) {
+			$opt [self::QUERY_STRING] ["time"] = $opt ["time"];
+		}
+		if (isset ( $opt ["size"] )) {
+			$opt [self::QUERY_STRING] ["size"] = $opt ["size"];
+		}
+		return $this->format_url ( $opt );
+	}
+
+	/**
+	 * 生成get_object的url
+	 * @param string $bucket (Required)
+	 * @param string $object (Required)
+	 * return false| string url
+	 */
+	public function generate_get_object_url($bucket, $object, $opt = array()) {
+		$this->assertParameterArray ( $opt );
+		return $this->generate_user_url ( "GET", $bucket, $object, $opt );
+	}
+
+	/**
+	 * 生成put_object的url
+	 * @param string $bucket (Required)
+	 * @param string $object (Required)
+	 * return false| string url
+	 */
+	public function generate_put_object_url($bucket, $object, $opt = array()) {
+		$this->assertParameterArray ( $opt );
+		return $this->generate_user_url ( "PUT", $bucket, $object, $opt );
+	}
+
+	/**
+	 * 生成post_object的url
+	 * @param string $bucket (Required)
+	 * @param string $object (Required)
+	 * return false| string url
+	 */
+	public function generate_post_object_url($bucket, $object, $opt = array()) {
+		$this->assertParameterArray ( $opt );
+		return $this->generate_user_url ( "POST", $bucket, $object, $opt );
+	}
+
+	/**
+	 * 生成delete_object的url
+	 * @param string $bucket (Required)
+	 * @param string $object (Required)
+	 * return false| string url
+	 */
+	public function generate_delete_object_url($bucket, $object, $opt = array()) {
+		$this->assertParameterArray ( $opt );
+		return $this->generate_user_url ( "DELETE", $bucket, $object, $opt );
+	}
+
+	/**
+	 * 生成head_object的url
+	 * @param string $bucket (Required)
+	 * @param string $object (Required)
+	 * return false| string url
+	 */
+	public function generate_head_object_url($bucket, $object, $opt = array()) {
+		$this->assertParameterArray ( $opt );
+		return $this->generate_user_url ( "HEAD", $bucket, $object, $opt );
+	}
+
+	/**
+	 * @return the $use_ssl
+	 */
+	public function getUse_ssl() {
+		return $this->use_ssl;
+	}
+
+	/**
+	 * @param boolean $use_ssl
+	 */
+	public function setUse_ssl($use_ssl) {
+		$this->use_ssl = $use_ssl;
+	}
+
+	/**
+	 * 校验bucket是否合法,bucket规范
+	 * 1. 由小写字母,数字和横线'-'组成,长度为6~63位 
+	 * 2. 不能以数字作为Bucket开头 
+	 * 3. 不能以'-'作为Bucket的开头或者结尾
+	 * @param string $bucket
+	 * @return boolean
+	 */
+	public static function validate_bucket($bucket) {
+		//bucket 正则
+		$pattern1 = '/^[a-z][-a-z0-9]{4,61}[a-z0-9]$/';
+		if (! preg_match ( $pattern1, $bucket )) {
+			return false;
+		}
+		return true;
+	}
+
+	/**
+	 * 校验object是否合法,object命名规范
+	 * 1. object必须以'/'开头
+	 * @param string $object
+	 * @return boolean
+	 */
+	public static function validate_object($object) {
+		$pattern = '/^\//';
+		if (empty ( $object ) || ! preg_match ( $pattern, $object )) {
+			return false;
+		}
+		return true;
+	}
+
+	/**
+	 * 将常用set http-header的动作抽离出来
+	 * @param string $header
+	 * @param string $value
+	 * @param array $opt
+	 * @throws BCS_Exception
+	 * @return void
+	 */
+	private static function set_header_into_opt($header, $value, &$opt) {
+		if (isset ( $opt [self::HEADERS] )) {
+			if (! is_array ( $opt [self::HEADERS] )) {
+				trigger_error ( 'Invalid $opt[\'headers\'], please check.' );
+				throw new BCS_Exception ( 'Invalid $opt[\'headers\'], please check.', - 1 );
+			}
+		} else {
+			$opt [self::HEADERS] = array ();
+		}
+		$opt [self::HEADERS] [$header] = $value;
+	}
+
+	/**
+	 * 使用特定function对数组中所有元素做处理
+	 * @param string    &$array        要处理的字符串
+	 * @param string    $function    要执行的函数
+	 * @param boolean   $apply_to_keys_also     是否也应用到key上
+	 */
+	private static function array_recursive(&$array, $function, $apply_to_keys_also = false) {
+		foreach ( $array as $key => $value ) {
+			if (is_array ( $value )) {
+				self::array_recursive ( $array [$key], $function, $apply_to_keys_also );
+			} else {
+				$array [$key] = $function ( $value );
+			}
+			
+			if ($apply_to_keys_also && is_string ( $key )) {
+				$new_key = $function ( $key );
+				if ($new_key != $key) {
+					$array [$new_key] = $array [$key];
+					unset ( $array [$key] );
+				}
+			}
+		}
+	}
+
+	/**
+	 * 由数组构造json字符串,增加了一些特殊处理以支持特殊字符和不同编码的中文
+	 * @param array $array
+	 */
+	private static function array_to_json($array) {
+		if (! is_array ( $array )) {
+			throw new BCS_Exception ( "Param must be array in function array_to_json()", - 1 );
+		}
+		self::array_recursive ( $array, 'addslashes', false );
+		self::array_recursive ( $array, 'rawurlencode', false );
+		return rawurldecode ( json_encode ( $array ) );
+	}
+
+	/**
+	 * 根据用户传入的acl,进行相应的处理
+	 * (1).设置详细json格式的acl;
+	 * a. $acl 为json的array
+	 * b. $acl 为json的string
+	 * (2).通过acl_type字段进行设置
+	 * @param string|array $acl
+	 * @throws BCS_Exception
+	 * @return array
+	 */
+	private function analyze_user_acl($acl) {
+		$result = array ();
+		if (is_array ( $acl )) {
+			//(1).a
+			$result ['content'] = $this->check_user_acl ( $acl );
+		} else if (is_string ( $acl )) {
+			if (in_array ( $acl, self::$ACL_TYPES )) {
+				//(2).a
+				$result ["headers"] = array (
+						"x-bs-acl" => $acl );
+			} else {
+				//(1).b
+				$result ['content'] = $acl;
+			}
+		} else {
+			throw new BCS_Exception ( "Invalid acl.", - 1 );
+		}
+		return $result;
+	}
+
+	/**
+	 * 生成签名
+	 * @param array $opt
+	 * @return boolean|string
+	 */
+	private function format_signature($opt) {
+		$flags = "";
+		$content = '';
+		if (! isset ( $opt [self::AK] ) || ! isset ( $opt [self::SK] )) {
+			trigger_error ( 'ak or sk is not in the array when create factor!' );
+			return false;
+		}
+		if (isset ( $opt [self::BUCKET] ) && isset ( $opt [self::METHOD] ) && isset ( $opt [self::OBJECT] )) {
+			$flags .= 'MBO';
+			$content .= "Method=" . $opt [self::METHOD] . "\n"; //method
+			$content .= "Bucket=" . $opt [self::BUCKET] . "\n"; //bucket
+			$content .= "Object=" . self::trimUrl ( $opt [self::OBJECT] ) . "\n"; //object
+		} else {
+			trigger_error ( 'bucket、method and object cann`t be NULL!' );
+			return false;
+		}
+		if (isset ( $opt ['ip'] )) {
+			$flags .= 'I';
+			$content .= "Ip=" . $opt ['ip'] . "\n";
+		}
+		if (isset ( $opt ['time'] )) {
+			$flags .= 'T';
+			$content .= "Time=" . $opt ['time'] . "\n";
+		}
+		if (isset ( $opt ['size'] )) {
+			$flags .= 'S';
+			$content .= "Size=" . $opt ['size'] . "\n";
+		}
+		$content = $flags . "\n" . $content;
+		$sign = base64_encode ( hash_hmac ( 'sha1', $content, $opt [self::SK], true ) );
+		return 'sign=' . $flags . ':' . $opt [self::AK] . ':' . urlencode ( $sign );
+	}
+
+	/**
+	 * 检查用户输入的acl array是否合法,并转为json
+	 * @param array $acl
+	 * @throws BCS_Exception
+	 * @return string acl-json
+	 */
+	private function check_user_acl($acl) {
+		if (! is_array ( $acl )) {
+			throw new BCS_Exception ( "Invalid acl array" );
+		}
+		foreach ( $acl ['statements'] as $key => $statement ) {
+			// user resource action effect must in statement
+			if (! isset ( $statement ['user'] ) || ! isset ( $statement ['resource'] ) || ! isset ( $statement ['action'] ) || ! isset ( $statement ['effect'] )) {
+				throw new BCS_Exception ( 'Param miss: format acl error, please check your param!' );
+			}
+			if (! is_array ( $statement ['user'] ) || ! is_array ( $statement ['resource'] )) {
+				throw new BCS_Exception ( 'Param error: user or resource must be array, please check your param!' );
+			}
+			if (! is_array ( $statement ['action'] ) || ! count ( array_diff ( $statement ['action'], self::$ACL_ACTIONS ) ) == 0) {
+				throw new BCS_Exception ( 'Param error: action, please check your param!' );
+			}
+			if (! in_array ( $statement ['effect'], self::$ACL_EFFECTS )) {
+				throw new BCS_Exception ( 'Param error: effect, please check your param!' );
+			}
+			if (isset ( $statement ['time'] )) {
+				if (! is_array ( $statement ['time'] )) {
+					throw new BCS_Exception ( 'Param error: time, please check your param!' );
+				}
+			}
+		}
+		
+		return self::array_to_json ( $acl );
+	}
+
+	/**
+	 * 构造url
+	 * @param array $opt
+	 * @return boolean|string
+	 */
+	private function format_url($opt) {
+		$sign = $this->format_signature ( $opt );
+		if ($sign === false) {
+			trigger_error ( "Format signature failed, please check!" );
+			return false;
+		}
+		$opt ['sign'] = $sign;
+		$url = "";
+		$url .= $this->use_ssl ? 'https://' : 'http://';
+		$url .= $this->hostname;
+		$url .= '/' . $opt [self::BUCKET];
+		if (isset ( $opt [self::OBJECT] ) && '/' !== $opt [self::OBJECT]) {
+			$url .= "/" . rawurlencode ( $opt [self::OBJECT] );
+		}
+		$url .= '?' . $sign;
+		if (isset ( $opt [self::QUERY_STRING] )) {
+			foreach ( $opt [self::QUERY_STRING] as $key => $value ) {
+				$url .= '&' . $key . '=' . $value;
+			}
+		}
+		return $url;
+	}
+
+	/**
+	 * 将url中 '//' 替换为  '/'
+	 * @param $url
+	 * @return string
+	 */
+	public static function trimUrl($url) {
+		$result = str_replace ( "//", "/", $url );
+		while ( $result !== $url ) {
+			$url = $result;
+			$result = str_replace ( "//", "/", $url );
+		}
+		return $result;
+	}
+
+	/**
+	 * 获取传入目录的文件列表
+	 * @param string $dir 文件目录
+	 * @return array 文件树
+	 */
+	public static function get_filetree($dir, $file_prefix = "/*") {
+		$tree = array ();
+		foreach ( glob ( $dir . $file_prefix ) as $single ) {
+			if (is_dir ( $single )) {
+				$tree = array_merge ( $tree, self::get_filetree ( $single ) );
+			} else {
+				$tree [] = $single;
+			}
+		}
+		return $tree;
+	}
+
+	/**
+	 * 内置的日志函数,可以根据用户传入的log函数,进行日志输出
+	 * @param string $log
+	 * @param array $opt
+	 */
+	public function log($log, $opt) {
+		if (isset ( $opt [self::IMPORT_BCS_LOG_METHOD] ) && function_exists ( $opt [self::IMPORT_BCS_LOG_METHOD] )) {
+			$opt [self::IMPORT_BCS_LOG_METHOD] ( $log );
+		} else {
+			trigger_error ( $log );
+		}
+	}
+
+	/**
+	 * make sure $opt is an array
+	 * @param $opt 
+	 */
+	private function assertParameterArray($opt) {
+		if (! is_array ( $opt )) {
+			throw new BCS_Exception ( 'Parameter must be array, please check!', - 1 );
+		}
+	}
+}

+ 9 - 0
include/storages/bcs/conf.inc.php

@@ -0,0 +1,9 @@
+<?php
+//AK 公钥
+define ( 'BCS_AK', defined('CS_AK')?CS_AK:getenv('HTTP_BAE_ENV_AK') );
+//SK 私钥
+define ( 'BCS_SK', defined('CS_SK')?CS_SK:getenv('HTTP_BAE_ENV_SK') );
+//superfile 每个object分片后缀
+define ( 'BCS_SUPERFILE_POSTFIX', '_bcs_superfile_' );
+//sdk superfile分片大小 ,单位 B(字节)
+define ( 'BCS_SUPERFILE_SLICE_SIZE', 1024 * 1024 );

+ 25 - 0
include/storages/bcs/libs/requestcore/LICENSE

@@ -0,0 +1,25 @@
+Copyright (c) 2006-2010 Ryan Parman, Foleeo Inc., and contributors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are
+permitted provided that the following conditions are met:
+
+	* Redistributions of source code must retain the above copyright notice, this list of
+	  conditions and the following disclaimer.
+
+	* Redistributions in binary form must reproduce the above copyright notice, this list
+	  of conditions and the following disclaimer in the documentation and/or other materials
+	  provided with the distribution.
+
+	* Neither the name of Ryan Parman, Foleeo Inc. nor the names of its contributors may be used to
+	  endorse or promote products derived from this software without specific prior written
+	  permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
+OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
+AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.

+ 15 - 0
include/storages/bcs/libs/requestcore/README.md

@@ -0,0 +1,15 @@
+# RequestCore
+
+RequestCore is a lightweight cURL-based HTTP request/response class that leverages MultiCurl for parallel requests.
+
+### PEAR HTTP_Request?
+
+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.
+
+### Reference and Download
+
+You can find the class reference at <http://skyzyx.github.com/requestcore/>. You can get the code from <http://github.com/skyzyx/requestcore>.
+
+### License and Copyright
+
+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).

+ 832 - 0
include/storages/bcs/libs/requestcore/requestcore.class.php

@@ -0,0 +1,832 @@
+<?php
+/**
+ * Handles all HTTP requests using cURL and manages the responses.
+ *
+ * @version 2011.03.01
+ * @copyright 2006-2011 Ryan Parman
+ * @copyright 2006-2010 Foleeo Inc.
+ * @copyright 2010-2011 Amazon.com, Inc. or its affiliates.
+ * @copyright 2008-2011 Contributors
+ * @license http://opensource.org/licenses/bsd-license.php Simplified BSD License
+ */
+class BCS_RequestCore {
+	/**
+	 * The URL being requested.
+	 */
+	public $request_url;
+	/**
+	 * The headers being sent in the request.
+	 */
+	public $request_headers;
+	/**
+	 * The body being sent in the request.
+	 */
+	public $request_body;
+	/**
+	 * The response returned by the request.
+	 */
+	public $response;
+	/**
+	 * The headers returned by the request.
+	 */
+	public $response_headers;
+	/**
+	 * The body returned by the request.
+	 */
+	public $response_body;
+	/**
+	 * The HTTP status code returned by the request.
+	 */
+	public $response_code;
+	/**
+	 * Additional response data.
+	 */
+	public $response_info;
+	/**
+	 * The handle for the cURL object.
+	 */
+	public $curl_handle;
+	/**
+	 * The method by which the request is being made.
+	 */
+	public $method;
+	/**
+	 * Stores the proxy settings to use for the request.
+	 */
+	public $proxy = null;
+	/**
+	 * The username to use for the request.
+	 */
+	public $username = null;
+	/**
+	 * The password to use for the request.
+	 */
+	public $password = null;
+	/**
+	 * Custom CURLOPT settings.
+	 */
+	public $curlopts = null;
+	/**
+	 * The state of debug mode.
+	 */
+	public $debug_mode = false;
+	/**
+	 * The default class to use for HTTP Requests (defaults to <BCS_RequestCore>).
+	 */
+	public $request_class = 'BCS_RequestCore';
+	/**
+	 * The default class to use for HTTP Responses (defaults to <BCS_ResponseCore>).
+	 */
+	public $response_class = 'BCS_ResponseCore';
+	/**
+	 * Default useragent string to use.
+	 */
+	public $useragent = 'BCS_RequestCore/1.4.2';
+	/**
+	 * File to read from while streaming up.
+	 */
+	public $read_file = null;
+	/**
+	 * The resource to read from while streaming up.
+	 */
+	public $read_stream = null;
+	/**
+	 * The size of the stream to read from.
+	 */
+	public $read_stream_size = null;
+	/**
+	 * The length already read from the stream.
+	 */
+	public $read_stream_read = 0;
+	/**
+	 * File to write to while streaming down.
+	 */
+	public $write_file = null;
+	/**
+	 * The resource to write to while streaming down.
+	 */
+	public $write_stream = null;
+	/**
+	 * Stores the intended starting seek position.
+	 */
+	public $seek_position = null;
+	/**
+	 * The user-defined callback function to call when a stream is read from.
+	 */
+	public $registered_streaming_read_callback = null;
+	/**
+	 * The user-defined callback function to call when a stream is written to.
+	 */
+	public $registered_streaming_write_callback = null;
+	/*%******************************************************************************************%*/
+	// CONSTANTS
+	/**
+	 * GET HTTP Method
+	 */
+	const HTTP_GET = 'GET';
+	/**
+	 * POST HTTP Method
+	 */
+	const HTTP_POST = 'POST';
+	/**
+	 * PUT HTTP Method
+	 */
+	const HTTP_PUT = 'PUT';
+	/**
+	 * DELETE HTTP Method
+	 */
+	const HTTP_DELETE = 'DELETE';
+	/**
+	 * HEAD HTTP Method
+	 */
+	const HTTP_HEAD = 'HEAD';
+
+	/*%******************************************************************************************%*/
+	// CONSTRUCTOR/DESTRUCTOR
+	/**
+	 * Constructs a new instance of this class.
+	 *
+	 * @param string $url (Optional) The URL to request or service endpoint to query.
+	 * @param string $proxy (Optional) The faux-url to use for proxy settings. Takes the following format: `proxy://user:pass@hostname:port`
+	 * @param array $helpers (Optional) An associative array of classnames to use for request, and response functionality. Gets passed in automatically by the calling class.
+	 * @return $this A reference to the current instance.
+	 */
+	public function __construct($url = null, $proxy = null, $helpers = null) {
+		// Set some default values.
+		$this->request_url = $url;
+		$this->method = self::HTTP_GET;
+		$this->request_headers = array ();
+		$this->request_body = '';
+		// Set a new Request class if one was set.
+		if (isset ( $helpers ['request'] ) && ! empty ( $helpers ['request'] )) {
+			$this->request_class = $helpers ['request'];
+		}
+		// Set a new Request class if one was set.
+		if (isset ( $helpers ['response'] ) && ! empty ( $helpers ['response'] )) {
+			$this->response_class = $helpers ['response'];
+		}
+		if ($proxy) {
+			$this->set_proxy ( $proxy );
+		}
+		return $this;
+	}
+
+	/**
+	 * Destructs the instance. Closes opened file handles.
+	 *
+	 * @return $this A reference to the current instance.
+	 */
+	public function __destruct() {
+		if (isset ( $this->read_file ) && isset ( $this->read_stream )) {
+			fclose ( $this->read_stream );
+		}
+		if (isset ( $this->write_file ) && isset ( $this->write_stream )) {
+			fclose ( $this->write_stream );
+		}
+		return $this;
+	}
+
+	/*%******************************************************************************************%*/
+	// REQUEST METHODS
+	/**
+	 * Sets the credentials to use for authentication.
+	 *
+	 * @param string $user (Required) The username to authenticate with.
+	 * @param string $pass (Required) The password to authenticate with.
+	 * @return $this A reference to the current instance.
+	 */
+	public function set_credentials($user, $pass) {
+		$this->username = $user;
+		$this->password = $pass;
+		return $this;
+	}
+
+	/**
+	 * Adds a custom HTTP header to the cURL request.
+	 *
+	 * @param string $key (Required) The custom HTTP header to set.
+	 * @param mixed $value (Required) The value to assign to the custom HTTP header.
+	 * @return $this A reference to the current instance.
+	 */
+	public function add_header($key, $value) {
+		$this->request_headers [$key] = $value;
+		return $this;
+	}
+
+	/**
+	 * Removes an HTTP header from the cURL request.
+	 *
+	 * @param string $key (Required) The custom HTTP header to set.
+	 * @return $this A reference to the current instance.
+	 */
+	public function remove_header($key) {
+		if (isset ( $this->request_headers [$key] )) {
+			unset ( $this->request_headers [$key] );
+		}
+		return $this;
+	}
+
+	/**
+	 * Set the method type for the request.
+	 *
+	 * @param string $method (Required) One of the following constants: <HTTP_GET>, <HTTP_POST>, <HTTP_PUT>, <HTTP_HEAD>, <HTTP_DELETE>.
+	 * @return $this A reference to the current instance.
+	 */
+	public function set_method($method) {
+		$this->method = strtoupper ( $method );
+		return $this;
+	}
+
+	/**
+	 * Sets a custom useragent string for the class.
+	 *
+	 * @param string $ua (Required) The useragent string to use.
+	 * @return $this A reference to the current instance.
+	 */
+	public function set_useragent($ua) {
+		$this->useragent = $ua;
+		return $this;
+	}
+
+	/**
+	 * Set the body to send in the request.
+	 *
+	 * @param string $body (Required) The textual content to send along in the body of the request.
+	 * @return $this A reference to the current instance.
+	 */
+	public function set_body($body) {
+		$this->request_body = $body;
+		return $this;
+	}
+
+	/**
+	 * Set the URL to make the request to.
+	 *
+	 * @param string $url (Required) The URL to make the request to.
+	 * @return $this A reference to the current instance.
+	 */
+	public function set_request_url($url) {
+		$this->request_url = $url;
+		return $this;
+	}
+
+	/**
+	 * Set additional CURLOPT settings. These will merge with the default settings, and override if
+	 * there is a duplicate.
+	 *
+	 * @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.
+	 * @return $this A reference to the current instance.
+	 */
+	public function set_curlopts($curlopts) {
+		$this->curlopts = $curlopts;
+		return $this;
+	}
+
+	/**
+	 * Sets the length in bytes to read from the stream while streaming up.
+	 *
+	 * @param integer $size (Required) The length in bytes to read from the stream.
+	 * @return $this A reference to the current instance.
+	 */
+	public function set_read_stream_size($size) {
+		$this->read_stream_size = $size;
+		return $this;
+	}
+
+	/**
+	 * Sets the resource to read from while streaming up. Reads the stream from its current position until
+	 * EOF or `$size` bytes have been read. If `$size` is not given it will be determined by <php:fstat()> and
+	 * <php:ftell()>.
+	 *
+	 * @param resource $resource (Required) The readable resource to read from.
+	 * @param integer $size (Optional) The size of the stream to read.
+	 * @return $this A reference to the current instance.
+	 */
+	public function set_read_stream($resource, $size = null) {
+		if (! isset ( $size ) || $size < 0) {
+			$stats = fstat ( $resource );
+			if ($stats && $stats ['size'] >= 0) {
+				$position = ftell ( $resource );
+				if ($position !== false && $position >= 0) {
+					$size = $stats ['size'] - $position;
+				}
+			}
+		}
+		$this->read_stream = $resource;
+		return $this->set_read_stream_size ( $size );
+	}
+
+	/**
+	 * Sets the file to read from while streaming up.
+	 *
+	 * @param string $location (Required) The readable location to read from.
+	 * @return $this A reference to the current instance.
+	 */
+	public function set_read_file($location) {
+		$this->read_file = $location;
+		$read_file_handle = fopen ( $location, 'r' );
+		return $this->set_read_stream ( $read_file_handle );
+	}
+
+	/**
+	 * Sets the resource to write to while streaming down.
+	 *
+	 * @param resource $resource (Required) The writeable resource to write to.
+	 * @return $this A reference to the current instance.
+	 */
+	public function set_write_stream($resource) {
+		$this->write_stream = $resource;
+		return $this;
+	}
+
+	/**
+	 * Sets the file to write to while streaming down.
+	 *
+	 * @param string $location (Required) The writeable location to write to.
+	 * @return $this A reference to the current instance.
+	 */
+	public function set_write_file($location) {
+		$this->write_file = $location;
+		$write_file_handle = fopen ( $location, 'w' );
+		return $this->set_write_stream ( $write_file_handle );
+	}
+
+	/**
+	 * Set the proxy to use for making requests.
+	 *
+	 * @param string $proxy (Required) The faux-url to use for proxy settings. Takes the following format: `proxy://user:pass@hostname:port`
+	 * @return $this A reference to the current instance.
+	 */
+	public function set_proxy($proxy) {
+		$proxy = parse_url ( $proxy );
+		$proxy ['user'] = isset ( $proxy ['user'] ) ? $proxy ['user'] : null;
+		$proxy ['pass'] = isset ( $proxy ['pass'] ) ? $proxy ['pass'] : null;
+		$proxy ['port'] = isset ( $proxy ['port'] ) ? $proxy ['port'] : null;
+		$this->proxy = $proxy;
+		return $this;
+	}
+
+	/**
+	 * Set the intended starting seek position.
+	 *
+	 * @param integer $position (Required) The byte-position of the stream to begin reading from.
+	 * @return $this A reference to the current instance.
+	 */
+	public function set_seek_position($position) {
+		$this->seek_position = isset ( $position ) ? ( integer ) $position : null;
+		return $this;
+	}
+
+	/**
+	 * Register a callback function to execute whenever a data stream is read from using
+	 * <CFRequest::streaming_read_callback()>.
+	 *
+	 * The user-defined callback function should accept three arguments:
+	 *
+	 * <ul>
+	 * <li><code>$curl_handle</code> - <code>resource</code> - Required - The cURL handle resource that represents the in-progress transfer.</li>
+	 * <li><code>$file_handle</code> - <code>resource</code> - Required - The file handle resource that represents the file on the local file system.</li>
+	 * <li><code>$length</code> - <code>integer</code> - Required - The length in kilobytes of the data chunk that was transferred.</li>
+	 * </ul>
+	 *
+	 * @param string|array|function $callback (Required) The callback function is called by <php:call_user_func()>, so you can pass the following values: <ul>
+	 * <li>The name of a global function to execute, passed as a string.</li>
+	 * <li>A method to execute, passed as <code>array('ClassName', 'MethodName')</code>.</li>
+	 * <li>An anonymous function (PHP 5.3+).</li></ul>
+	 * @return $this A reference to the current instance.
+	 */
+	public function register_streaming_read_callback($callback) {
+		$this->registered_streaming_read_callback = $callback;
+		return $this;
+	}
+
+	/**
+	 * Register a callback function to execute whenever a data stream is written to using
+	 * <CFRequest::streaming_write_callback()>.
+	 *
+	 * The user-defined callback function should accept two arguments:
+	 *
+	 * <ul>
+	 * <li><code>$curl_handle</code> - <code>resource</code> - Required - The cURL handle resource that represents the in-progress transfer.</li>
+	 * <li><code>$length</code> - <code>integer</code> - Required - The length in kilobytes of the data chunk that was transferred.</li>
+	 * </ul>
+	 *
+	 * @param string|array|function $callback (Required) The callback function is called by <php:call_user_func()>, so you can pass the following values: <ul>
+	 * <li>The name of a global function to execute, passed as a string.</li>
+	 * <li>A method to execute, passed as <code>array('ClassName', 'MethodName')</code>.</li>
+	 * <li>An anonymous function (PHP 5.3+).</li></ul>
+	 * @return $this A reference to the current instance.
+	 */
+	public function register_streaming_write_callback($callback) {
+		$this->registered_streaming_write_callback = $callback;
+		return $this;
+	}
+
+	/*%******************************************************************************************%*/
+	// PREPARE, SEND, AND PROCESS REQUEST
+	/**
+	 * A callback function that is invoked by cURL for streaming up.
+	 *
+	 * @param resource $curl_handle (Required) The cURL handle for the request.
+	 * @param resource $file_handle (Required) The open file handle resource.
+	 * @param integer $length (Required) The maximum number of bytes to read.
+	 * @return binary Binary data from a stream.
+	 */
+	public function streaming_read_callback($curl_handle, $file_handle, $length) {
+		// Once we've sent as much as we're supposed to send...
+		if ($this->read_stream_read >= $this->read_stream_size) {
+			// Send EOF
+			return '';
+		}
+		// If we're at the beginning of an upload and need to seek...
+		if ($this->read_stream_read == 0 && isset ( $this->seek_position ) && $this->seek_position !== ftell ( $this->read_stream )) {
+			if (fseek ( $this->read_stream, $this->seek_position ) !== 0) {
+				throw new BCS_RequestCore_Exception ( 'The stream does not support seeking and is either not at the requested position or the position is unknown.' );
+			}
+		}
+		$read = fread ( $this->read_stream, min ( $this->read_stream_size - $this->read_stream_read, $length ) ); // Remaining upload data or cURL's requested chunk size
+		$this->read_stream_read += strlen ( $read );
+		$out = $read === false ? '' : $read;
+		// Execute callback function
+		if ($this->registered_streaming_read_callback) {
+			call_user_func ( $this->registered_streaming_read_callback, $curl_handle, $file_handle, $out );
+		}
+		return $out;
+	}
+
+	/**
+	 * A callback function that is invoked by cURL for streaming down.
+	 *
+	 * @param resource $curl_handle (Required) The cURL handle for the request.
+	 * @param binary $data (Required) The data to write.
+	 * @return integer The number of bytes written.
+	 */
+	public function streaming_write_callback($curl_handle, $data) {
+		$length = strlen ( $data );
+		$written_total = 0;
+		$written_last = 0;
+		while ( $written_total < $length ) {
+			$written_last = fwrite ( $this->write_stream, substr ( $data, $written_total ) );
+			if ($written_last === false) {
+				return $written_total;
+			}
+			$written_total += $written_last;
+		}
+		// Execute callback function
+		if ($this->registered_streaming_write_callback) {
+			call_user_func ( $this->registered_streaming_write_callback, $curl_handle, $written_total );
+		}
+		return $written_total;
+	}
+
+	/**
+	 * Prepares and adds the details of the cURL request. This can be passed along to a <php:curl_multi_exec()>
+	 * function.
+	 *
+	 * @return resource The handle for the cURL object.
+	 */
+	public function prep_request() {
+		$curl_handle = curl_init ();
+		// Set default options.
+		curl_setopt ( $curl_handle, CURLOPT_URL, $this->request_url );
+		curl_setopt ( $curl_handle, CURLOPT_FILETIME, true );
+		curl_setopt ( $curl_handle, CURLOPT_FRESH_CONNECT, false );
+		curl_setopt ( $curl_handle, CURLOPT_SSL_VERIFYPEER, false );
+		curl_setopt ( $curl_handle, CURLOPT_SSL_VERIFYHOST, true );
+		curl_setopt ( $curl_handle, CURLOPT_CLOSEPOLICY, CURLCLOSEPOLICY_LEAST_RECENTLY_USED );
+		curl_setopt ( $curl_handle, CURLOPT_MAXREDIRS, 5 );
+		curl_setopt ( $curl_handle, CURLOPT_HEADER, true );
+		curl_setopt ( $curl_handle, CURLOPT_RETURNTRANSFER, true );
+		curl_setopt ( $curl_handle, CURLOPT_TIMEOUT, 5184000 );
+		curl_setopt ( $curl_handle, CURLOPT_CONNECTTIMEOUT, 120 );
+		curl_setopt ( $curl_handle, CURLOPT_NOSIGNAL, true );
+		curl_setopt ( $curl_handle, CURLOPT_REFERER, $this->request_url );
+		curl_setopt ( $curl_handle, CURLOPT_USERAGENT, $this->useragent );
+		curl_setopt ( $curl_handle, CURLOPT_READFUNCTION, array (
+				$this, 
+				'streaming_read_callback' ) );
+		if ($this->debug_mode) {
+			curl_setopt ( $curl_handle, CURLOPT_VERBOSE, true );
+		}
+		//if (! ini_get ( 'safe_mode' )) {
+		//modify by zhengkan
+		//curl_setopt($curl_handle, CURLOPT_FOLLOWLOCATION, true);
+		//}
+		// Enable a proxy connection if requested.
+		if ($this->proxy) {
+			curl_setopt ( $curl_handle, CURLOPT_HTTPPROXYTUNNEL, true );
+			$host = $this->proxy ['host'];
+			$host .= ($this->proxy ['port']) ? ':' . $this->proxy ['port'] : '';
+			curl_setopt ( $curl_handle, CURLOPT_PROXY, $host );
+			if (isset ( $this->proxy ['user'] ) && isset ( $this->proxy ['pass'] )) {
+				curl_setopt ( $curl_handle, CURLOPT_PROXYUSERPWD, $this->proxy ['user'] . ':' . $this->proxy ['pass'] );
+			}
+		}
+		// Set credentials for HTTP Basic/Digest Authentication.
+		if ($this->username && $this->password) {
+			curl_setopt ( $curl_handle, CURLOPT_HTTPAUTH, CURLAUTH_ANY );
+			curl_setopt ( $curl_handle, CURLOPT_USERPWD, $this->username . ':' . $this->password );
+		}
+		// Handle the encoding if we can.
+		if (extension_loaded ( 'zlib' )) {
+			curl_setopt ( $curl_handle, CURLOPT_ENCODING, '' );
+		}
+		// Process custom headers
+		if (isset ( $this->request_headers ) && count ( $this->request_headers )) {
+			$temp_headers = array ();
+			foreach ( $this->request_headers as $k => $v ) {
+				$temp_headers [] = $k . ': ' . $v;
+			}
+			curl_setopt ( $curl_handle, CURLOPT_HTTPHEADER, $temp_headers );
+		}
+		switch ($this->method) {
+			case self::HTTP_PUT :
+				curl_setopt ( $curl_handle, CURLOPT_CUSTOMREQUEST, 'PUT' );
+				if (isset ( $this->read_stream )) {
+					if (! isset ( $this->read_stream_size ) || $this->read_stream_size < 0) {
+						throw new BCS_RequestCore_Exception ( 'The stream size for the streaming upload cannot be determined.' );
+					}
+					curl_setopt ( $curl_handle, CURLOPT_INFILESIZE, $this->read_stream_size );
+					curl_setopt ( $curl_handle, CURLOPT_UPLOAD, true );
+				} else {
+					curl_setopt ( $curl_handle, CURLOPT_POSTFIELDS, $this->request_body );
+				}
+				break;
+			case self::HTTP_POST :
+				curl_setopt ( $curl_handle, CURLOPT_POST, true );
+				curl_setopt ( $curl_handle, CURLOPT_POSTFIELDS, $this->request_body );
+				break;
+			case self::HTTP_HEAD :
+				curl_setopt ( $curl_handle, CURLOPT_CUSTOMREQUEST, self::HTTP_HEAD );
+				curl_setopt ( $curl_handle, CURLOPT_NOBODY, 1 );
+				break;
+			default : // Assumed GET
+				curl_setopt ( $curl_handle, CURLOPT_CUSTOMREQUEST, $this->method );
+				if (isset ( $this->write_stream )) {
+					curl_setopt ( $curl_handle, CURLOPT_WRITEFUNCTION, array (
+							$this, 
+							'streaming_write_callback' ) );
+					curl_setopt ( $curl_handle, CURLOPT_HEADER, false );
+				} else {
+					curl_setopt ( $curl_handle, CURLOPT_POSTFIELDS, $this->request_body );
+				}
+				break;
+		}
+		// Merge in the CURLOPTs
+		if (isset ( $this->curlopts ) && sizeof ( $this->curlopts ) > 0) {
+			foreach ( $this->curlopts as $k => $v ) {
+				curl_setopt ( $curl_handle, $k, $v );
+			}
+		}
+		return $curl_handle;
+	}
+
+	/**
+	 * is the environment BAE?
+	 * @return boolean the result of the answer
+	 */
+	private function isBaeEnv() {
+		if (isset ( $_SERVER ['HTTP_HOST'] )) {
+			$host = $_SERVER ['HTTP_HOST'];
+			$pos = strpos ( $host, '.' );
+			if ($pos !== false) {
+				$substr = substr ( $host, $pos + 1 );
+				if ($substr == 'duapp.com') {
+					return true;
+				}
+			}
+		}
+		if (isset ( $_SERVER ["HTTP_BAE_LOGID"] )) {
+			return true;
+		}
+		
+		return false;
+	}
+
+	/**
+	 * Take the post-processed cURL data and break it down into useful header/body/info chunks. Uses the
+	 * data stored in the `curl_handle` and `response` properties unless replacement data is passed in via
+	 * parameters.
+	 *
+	 * @param resource $curl_handle (Optional) The reference to the already executed cURL request.
+	 * @param string $response (Optional) The actual response content itself that needs to be parsed.
+	 * @return BCS_ResponseCore A <BCS_ResponseCore> object containing a parsed HTTP response.
+	 */
+	public function process_response($curl_handle = null, $response = null) {
+		// Accept a custom one if it's passed.
+		if ($curl_handle && $response) {
+			$this->curl_handle = $curl_handle;
+			$this->response = $response;
+		}
+		// As long as this came back as a valid resource...
+		if (is_resource ( $this->curl_handle )) {
+			// Determine what's what.
+			$header_size = curl_getinfo ( $this->curl_handle, CURLINFO_HEADER_SIZE );
+			$this->response_headers = substr ( $this->response, 0, $header_size );
+			$this->response_body = substr ( $this->response, $header_size );
+			$this->response_code = curl_getinfo ( $this->curl_handle, CURLINFO_HTTP_CODE );
+			$this->response_info = curl_getinfo ( $this->curl_handle );
+			// Parse out the headers
+			$this->response_headers = explode ( "\r\n\r\n", trim ( $this->response_headers ) );
+			$this->response_headers = array_pop ( $this->response_headers );
+			$this->response_headers = explode ( "\r\n", $this->response_headers );
+			array_shift ( $this->response_headers );
+			// Loop through and split up the headers.
+			$header_assoc = array ();
+			foreach ( $this->response_headers as $header ) {
+				$kv = explode ( ': ', $header );
+				//$header_assoc [strtolower ( $kv [0] )] = $kv [1];
+				$header_assoc [$kv [0]] = $kv [1];
+			}
+			// Reset the headers to the appropriate property.
+			$this->response_headers = $header_assoc;
+			$this->response_headers ['_info'] = $this->response_info;
+			$this->response_headers ['_info'] ['method'] = $this->method;
+			if ($curl_handle && $response) {
+				return new $this->response_class ( $this->response_headers, $this->response_body, $this->response_code, $this->curl_handle );
+			}
+		}
+		// Return false
+		return false;
+	}
+
+	/**
+	 * Sends the request, calling necessary utility functions to update built-in properties.
+	 *
+	 * @param boolean $parse (Optional) Whether to parse the response with BCS_ResponseCore or not.
+	 * @return string The resulting unparsed data from the request.
+	 */
+	public function send_request($parse = false) {
+		if (false === $this->isBaeEnv ()) {
+			set_time_limit ( 0 );
+		}
+		$curl_handle = $this->prep_request ();
+		$this->response = curl_exec ( $curl_handle );
+		if ($this->response === false) {
+			throw new BCS_RequestCore_Exception ( 'cURL resource: ' . ( string ) $curl_handle . '; cURL error: ' . curl_error ( $curl_handle ) . ' (' . curl_errno ( $curl_handle ) . ')' );
+		}
+		$parsed_response = $this->process_response ( $curl_handle, $this->response );
+		curl_close ( $curl_handle );
+		if ($parse) {
+			return $parsed_response;
+		}
+		return $this->response;
+	}
+
+	/**
+	 * Sends the request using <php:curl_multi_exec()>, enabling parallel requests. Uses the "rolling" method.
+	 *
+	 * @param array $handles (Required) An indexed array of cURL handles to process simultaneously.
+	 * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
+	 * <li><code>callback</code> - <code>string|array</code> - Optional - The string name of a function to pass the response data to. If this is a method, pass an array where the <code>[0]</code> index is the class and the <code>[1]</code> index is the method name.</li>
+	 * <li><code>limit</code> - <code>integer</code> - 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.</li></ul>
+	 * @return array Post-processed cURL responses.
+	 */
+	public function send_multi_request($handles, $opt = null) {
+		if (false === $this->isBaeEnv ()) {
+			set_time_limit ( 0 );
+		}
+		// Skip everything if there are no handles to process.
+		if (count ( $handles ) === 0)
+			return array ();
+		if (! $opt)
+			$opt = array ();
+		
+		// Initialize any missing options
+		$limit = isset ( $opt ['limit'] ) ? $opt ['limit'] : - 1;
+		// Initialize
+		$handle_list = $handles;
+		$http = new $this->request_class ();
+		$multi_handle = curl_multi_init ();
+		$handles_post = array ();
+		$added = count ( $handles );
+		$last_handle = null;
+		$count = 0;
+		$i = 0;
+		// Loop through the cURL handles and add as many as it set by the limit parameter.
+		while ( $i < $added ) {
+			if ($limit > 0 && $i >= $limit)
+				break;
+			curl_multi_add_handle ( $multi_handle, array_shift ( $handles ) );
+			$i ++;
+		}
+		do {
+			$active = false;
+			// Start executing and wait for a response.
+			while ( ($status = curl_multi_exec ( $multi_handle, $active )) === CURLM_CALL_MULTI_PERFORM ) {
+				// Start looking for possible responses immediately when we have to add more handles
+				if (count ( $handles ) > 0)
+					break;
+			}
+			// Figure out which requests finished.
+			$to_process = array ();
+			while ( $done = curl_multi_info_read ( $multi_handle ) ) {
+				// 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 )
+				if ($done ['result'] > 0) {
+					throw new BCS_RequestCore_Exception ( 'cURL resource: ' . ( string ) $done ['handle'] . '; cURL error: ' . curl_error ( $done ['handle'] ) . ' (' . $done ['result'] . ')' );
+				} // 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
+elseif (! isset ( $to_process [( int ) $done ['handle']] )) {
+					$to_process [( int ) $done ['handle']] = $done;
+				}
+			}
+			// Actually deal with the request
+			foreach ( $to_process as $pkey => $done ) {
+				$response = $http->process_response ( $done ['handle'], curl_multi_getcontent ( $done ['handle'] ) );
+				$key = array_search ( $done ['handle'], $handle_list, true );
+				$handles_post [$key] = $response;
+				if (count ( $handles ) > 0) {
+					curl_multi_add_handle ( $multi_handle, array_shift ( $handles ) );
+				}
+				curl_multi_remove_handle ( $multi_handle, $done ['handle'] );
+				curl_close ( $done ['handle'] );
+			}
+		} while ( $active || count ( $handles_post ) < $added );
+		curl_multi_close ( $multi_handle );
+		ksort ( $handles_post, SORT_NUMERIC );
+		return $handles_post;
+	}
+
+	/*%******************************************************************************************%*/
+	// RESPONSE METHODS
+	/**
+	 * Get the HTTP response headers from the request.
+	 *
+	 * @param string $header (Optional) A specific header value to return. Defaults to all headers.
+	 * @return string|array All or selected header values.
+	 */
+	public function get_response_header($header = null) {
+		if ($header) {
+			//			return $this->response_headers [strtolower ( $header )];
+			return $this->response_headers [$header];
+		}
+		return $this->response_headers;
+	}
+
+	/**
+	 * Get the HTTP response body from the request.
+	 *
+	 * @return string The response body.
+	 */
+	public function get_response_body() {
+		return $this->response_body;
+	}
+
+	/**
+	 * Get the HTTP response code from the request.
+	 *
+	 * @return string The HTTP response code.
+	 */
+	public function get_response_code() {
+		return $this->response_code;
+	}
+}
+/**
+ * Container for all response-related methods.
+ */
+class BCS_ResponseCore {
+	/**
+	 * Stores the HTTP header information.
+	 */
+	public $header;
+	/**
+	 * Stores the SimpleXML response.
+	 */
+	public $body;
+	/**
+	 * Stores the HTTP response code.
+	 */
+	public $status;
+
+	/**
+	 * Constructs a new instance of this class.
+	 *
+	 * @param array $header (Required) Associative array of HTTP headers (typically returned by <BCS_RequestCore::get_response_header()>).
+	 * @param string $body (Required) XML-formatted response from AWS.
+	 * @param integer $status (Optional) HTTP response status code from the request.
+	 * @return object Contains an <php:array> `header` property (HTTP headers as an associative array), a <php:SimpleXMLElement> or <php:string> `body` property, and an <php:integer> `status` code.
+	 */
+	public function __construct($header, $body, $status = null) {
+		$this->header = $header;
+		$this->body = $body;
+		$this->status = $status;
+		return $this;
+	}
+
+	/**
+	 * Did we receive the status code we expected?
+	 *
+	 * @param integer|array $codes (Optional) The status code(s) to expect. Pass an <php:integer> for a single acceptable value, or an <php:array> of integers for multiple acceptable values.
+	 * @return boolean Whether we received the expected status code or not.
+	 */
+	public function isOK($codes = array(200, 201, 204, 206)) {
+		if (is_array ( $codes )) {
+			return in_array ( $this->status, $codes );
+		}
+		return $this->status === $codes;
+	}
+}
+/**
+ * Default BCS_RequestCore Exception.
+ */
+class BCS_RequestCore_Exception extends Exception {
+}

+ 136 - 0
include/storages/bcs/utils/mimetypes.class.php

@@ -0,0 +1,136 @@
+<?php
+class BCS_MimeTypes {
+	public static $mime_types = array (
+			'3gp' => 'video/3gpp', 'ai' => 'application/postscript', 
+			'aif' => 'audio/x-aiff', 'aifc' => 'audio/x-aiff', 
+			'aiff' => 'audio/x-aiff', 'asc' => 'text/plain', 
+			'atom' => 'application/atom+xml', 'au' => 'audio/basic', 
+			'avi' => 'video/x-msvideo', 'bcpio' => 'application/x-bcpio', 
+			'bin' => 'application/octet-stream', 'bmp' => 'image/bmp', 
+			'cdf' => 'application/x-netcdf', 'cgm' => 'image/cgm', 
+			'class' => 'application/octet-stream', 
+			'cpio' => 'application/x-cpio', 
+			'cpt' => 'application/mac-compactpro', 
+			'csh' => 'application/x-csh', 'css' => 'text/css', 
+			'dcr' => 'application/x-director', 'dif' => 'video/x-dv', 
+			'dir' => 'application/x-director', 'djv' => 'image/vnd.djvu', 
+			'djvu' => 'image/vnd.djvu', 
+			'dll' => 'application/octet-stream', 
+			'dmg' => 'application/octet-stream', 
+			'dms' => 'application/octet-stream', 
+			'doc' => 'application/msword', 'dtd' => 'application/xml-dtd', 
+			'dv' => 'video/x-dv', 'dvi' => 'application/x-dvi', 
+			'dxr' => 'application/x-director', 
+			'eps' => 'application/postscript', 'etx' => 'text/x-setext', 
+			'exe' => 'application/octet-stream', 
+			'ez' => 'application/andrew-inset', 'flv' => 'video/x-flv', 
+			'gif' => 'image/gif', 'gram' => 'application/srgs', 
+			'grxml' => 'application/srgs+xml', 
+			'gtar' => 'application/x-gtar', 'gz' => 'application/x-gzip', 
+			'hdf' => 'application/x-hdf', 
+			'hqx' => 'application/mac-binhex40', 'htm' => 'text/html', 
+			'html' => 'text/html', 'ice' => 'x-conference/x-cooltalk', 
+			'ico' => 'image/x-icon', 'ics' => 'text/calendar', 
+			'ief' => 'image/ief', 'ifb' => 'text/calendar', 
+			'iges' => 'model/iges', 'igs' => 'model/iges', 
+			'jnlp' => 'application/x-java-jnlp-file', 'jp2' => 'image/jp2', 
+			'jpe' => 'image/jpeg', 'jpeg' => 'image/jpeg', 
+			'jpg' => 'image/jpeg', 'js' => 'application/x-javascript', 
+			'kar' => 'audio/midi', 'latex' => 'application/x-latex', 
+			'lha' => 'application/octet-stream', 
+			'lzh' => 'application/octet-stream', 
+			'm3u' => 'audio/x-mpegurl', 'm4a' => 'audio/mp4a-latm', 
+			'm4p' => 'audio/mp4a-latm', 'm4u' => 'video/vnd.mpegurl', 
+			'm4v' => 'video/x-m4v', 'mac' => 'image/x-macpaint', 
+			'man' => 'application/x-troff-man', 
+			'mathml' => 'application/mathml+xml', 
+			'me' => 'application/x-troff-me', 'mesh' => 'model/mesh', 
+			'mid' => 'audio/midi', 'midi' => 'audio/midi', 
+			'mif' => 'application/vnd.mif', 'mov' => 'video/quicktime', 
+			'movie' => 'video/x-sgi-movie', 'mp2' => 'audio/mpeg', 
+			'mp3' => 'audio/mpeg', 'mp4' => 'video/mp4', 
+			'mpe' => 'video/mpeg', 'mpeg' => 'video/mpeg', 
+			'mpg' => 'video/mpeg', 'mpga' => 'audio/mpeg', 
+			'ms' => 'application/x-troff-ms', 'msh' => 'model/mesh', 
+			'mxu' => 'video/vnd.mpegurl', 'nc' => 'application/x-netcdf', 
+			'oda' => 'application/oda', 'ogg' => 'application/ogg', 
+			'ogv' => 'video/ogv', 'pbm' => 'image/x-portable-bitmap', 
+			'pct' => 'image/pict', 'pdb' => 'chemical/x-pdb', 
+			'pdf' => 'application/pdf', 
+			'pgm' => 'image/x-portable-graymap', 
+			'pgn' => 'application/x-chess-pgn', 'pic' => 'image/pict', 
+			'pict' => 'image/pict', 'png' => 'image/png', 
+			'pnm' => 'image/x-portable-anymap', 
+			'pnt' => 'image/x-macpaint', 'pntg' => 'image/x-macpaint', 
+			'ppm' => 'image/x-portable-pixmap', 
+			'ppt' => 'application/vnd.ms-powerpoint', 
+			'ps' => 'application/postscript', 'qt' => 'video/quicktime', 
+			'qti' => 'image/x-quicktime', 'qtif' => 'image/x-quicktime', 
+			'ra' => 'audio/x-pn-realaudio', 
+			'ram' => 'audio/x-pn-realaudio', 'ras' => 'image/x-cmu-raster', 
+			'rdf' => 'application/rdf+xml', 'rgb' => 'image/x-rgb', 
+			'rm' => 'application/vnd.rn-realmedia', 
+			'roff' => 'application/x-troff', 'rtf' => 'text/rtf', 
+			'rtx' => 'text/richtext', 'sgm' => 'text/sgml', 
+			'sgml' => 'text/sgml', 'sh' => 'application/x-sh', 
+			'shar' => 'application/x-shar', 'silo' => 'model/mesh', 
+			'sit' => 'application/x-stuffit', 
+			'skd' => 'application/x-koan', 'skm' => 'application/x-koan', 
+			'skp' => 'application/x-koan', 'skt' => 'application/x-koan', 
+			'smi' => 'application/smil', 'smil' => 'application/smil', 
+			'snd' => 'audio/basic', 'so' => 'application/octet-stream', 
+			'spl' => 'application/x-futuresplash', 
+			'src' => 'application/x-wais-source', 
+			'sv4cpio' => 'application/x-sv4cpio', 
+			'sv4crc' => 'application/x-sv4crc', 'svg' => 'image/svg+xml', 
+			'swf' => 'application/x-shockwave-flash', 
+			't' => 'application/x-troff', 'tar' => 'application/x-tar', 
+			'tcl' => 'application/x-tcl', 'tex' => 'application/x-tex', 
+			'texi' => 'application/x-texinfo', 
+			'texinfo' => 'application/x-texinfo', 'tif' => 'image/tiff', 
+			'tiff' => 'image/tiff', 'tr' => 'application/x-troff', 
+			'tsv' => 'text/tab-separated-values', 'txt' => 'text/plain', 
+			'ustar' => 'application/x-ustar', 
+			'vcd' => 'application/x-cdlink', 'vrml' => 'model/vrml', 
+			'vxml' => 'application/voicexml+xml', 'wav' => 'audio/x-wav', 
+			'wbmp' => 'image/vnd.wap.wbmp', 
+			'wbxml' => 'application/vnd.wap.wbxml', 'webm' => 'video/webm', 
+			'wml' => 'text/vnd.wap.wml', 
+			'wmlc' => 'application/vnd.wap.wmlc', 
+			'wmls' => 'text/vnd.wap.wmlscript', 
+			'wmlsc' => 'application/vnd.wap.wmlscriptc', 
+			'wmv' => 'video/x-ms-wmv', 'wrl' => 'model/vrml', 
+			'xbm' => 'image/x-xbitmap', 'xht' => 'application/xhtml+xml', 
+			'xhtml' => 'application/xhtml+xml', 
+			'xls' => 'application/vnd.ms-excel', 
+			'xml' => 'application/xml', 'xpm' => 'image/x-xpixmap', 
+			'xsl' => 'application/xml', 'xslt' => 'application/xslt+xml', 
+			'xul' => 'application/vnd.mozilla.xul+xml', 
+			'xwd' => 'image/x-xwindowdump', 'xyz' => 'chemical/x-xyz', 
+			'zip' => 'application/zip', 
+			//add by zhengkan 20110905
+			"apk" => "application/vnd.android.package-archive", 
+			"bin" => "application/octet-stream", 
+			"cab" => "application/vnd.ms-cab-compressed", 
+			"gb" => "application/chinese-gb", 
+			"gba" => "application/octet-stream", 
+			"gbc" => "application/octet-stream", 
+			"jad" => "text/vnd.sun.j2me.app-descriptor", 
+			"jar" => "application/java-archive", 
+			"nes" => "application/octet-stream", 
+			"rar" => "application/x-rar-compressed", 
+			"sis" => "application/vnd.symbian.install", 
+			"sisx" => "x-epoc/x-sisx-app", 
+			"smc" => "application/octet-stream", 
+			"smd" => "application/octet-stream", 
+			"swf" => "application/x-shockwave-flash", 
+			"zip" => "application/x-zip-compressed", 
+			"wap" => "text/vnd.wap.wml wml", "mrp" => "application/mrp", 
+			//add by zhengkan 20110914
+			"wma" => "audio/x-ms-wma", 
+			"lrc" => "application/lrc" );
+	public static function get_mimetype($ext) {
+		$ext = strtolower ( $ext );
+		return (isset ( self::$mime_types [$ext] ) ? self::$mime_types [$ext] : 'application/octet-stream');
+	}
+}

+ 2492 - 0
include/storages/gcs/GrandCloudStorage.php

@@ -0,0 +1,2492 @@
+<?php
+/**
+ * =======================================================================
+ * simple:
+ *   require_once (dirname(__FILE__).'/GrandCloudStorage.php');
+ *
+ *   $client = new GrandCloudStorage($host);
+ *   $client->set_key_secret($access_key, $access_secret);
+ *
+ *   $bucket_name = "test_bucket";
+ *   $client->put_bucket($bucket_name); // create new bucket
+ *
+ *   $client->set_bucket($bucket_name); // set $bucket_name as default for follow ops
+ *   $client->put_object("test.ext", "localfile.ext"); // upload localfile.ext file to $bucket_name and assign name as text.ext
+ *   $client->head_object("test.ext"); // get test.ext object's meta
+ *   $client->get_object("test.ext", "tmp.ext"); // download test.ext object as local tmp.ext file
+ *   $client->delete_object("test.ext"); // delete test.ext object
+ * =======================================================================
+ */
+
+// Errors
+define('ERR_ACCESS_DENIED', 'AccessDenied');
+define('ERR_UNSUPPORTED_TRANSFER_ENCODING', 'UnsupportedTransferEncoding');
+define('ERR_BAD_DIGEST', 'BadDigest');
+define('ERR_INCOMPLETE_BODY', 'IncompleteBody');
+define('ERR_BUCKET_ACCESS_DENIED', 'BucketAccessDenied');
+define('ERR_BUCKET_NOT_EMPTY', 'BucketNotEmpty');
+define('ERR_BUCKET_UNEXIST', 'NoSuchBucket');
+define('ERR_BUCKET_TOO_MANY', 'TooManyBuckets');
+define('ERR_BUCKET_NAME_CONFLICT', 'BucketAlreadyExists');
+define('ERR_BUCKET_NAME_INVALID', 'InvalidBucketName');
+define('ERR_BUCKET_NOPOLICY', 'NotSuchBucketPolicy');
+define('ERR_ENTITY_TOO_LARGE', 'EntityTooLarge');
+define('ERR_OBJECT_KEY_TOO_LONG', 'KeyTooLong');
+define('ERR_OBJECT_UNEXIST', 'NoSuchKey');
+define('ERR_INVALID_ACCESS_KEY', 'InvalidAccessKeyId');
+define('ERR_INVALID_CONTENT_LENGTH', 'InvalidContentLength');
+define('ERR_INVALID_EXPIRES', 'InvalidExpires');
+define('ERR_INVALID_RANGE', 'InvalidRange');
+define('ERR_INVALID_REQUEST_TIME', 'InvalidRequestTime');
+define('ERR_INVALID_USER_METADATA', 'InvalidUserMetadata');
+define('ERR_MALFORMED_AUTHORIZATION', 'MalformedAuthorization');
+define('ERR_MALFORMED_XML', 'MalformedXML');
+define('ERR_METHOD_NOT_ALLOWED', 'MethodNotAllowed');
+define('ERR_MISSING_CONTENT_LENGTH', 'MissingContentLength');
+define('ERR_MISSING_SECURITY_HEADER', 'MissingSecurityHeader');
+define('ERR_MULTIPLE_RANGE', 'MultipleRange');
+define('ERR_PRECONDITION_FAILED', 'PreconditionFailed');
+define('ERR_REQUEST_EXPIRED', 'RequestHasExpired');
+define('ERR_REQUEST_TIMEOUT', 'RequestTimeout');
+define('ERR_REQUEST_TIMESKEWED', 'RequestTimeTooSkewed');
+define('ERR_SIGNATURE_UNMATCH', 'SignatureDoesNotMatch');
+
+
+// Bucket对象
+class GCBucket {
+	protected $idc; // bucket所在IDC,可用值为:huadong-1,huabei-1
+	protected $name; // bucket名称
+	protected $ctime; // bucket创建时间,参见:date('r')
+
+	public function __construct($idc, $name, $ctime) {
+		$this->idc = $idc;
+		$this->name = $name;
+		$this->ctime = $ctime;
+
+		return $this;
+	}
+
+	public function get_idc() {
+		return $this->idc;
+	}
+
+	public function get_name() {
+		return $this->name;
+	}
+
+	public function get_ctime() {
+		return $this->ctime;
+	}
+
+	public function to_array() {
+		return array(
+				'idc' => $this->idc,
+				'name' => $this->name,
+				'ctime' => $this->ctime
+		);
+	}
+}
+
+// Object对象
+class GCObject {
+	protected $key; // object key
+	protected $size; // object size
+	protected $etag; // object ETAG
+	protected $last_modified; // object last modified time
+
+	public function __construct($key, $size, $last_modified, $etag) {
+		$this->key = $key;
+		$this->size = $size;
+		$this->etag = $etag;
+		$this->last_modified = $last_modified;
+
+		return $this;
+	}
+
+	public function get_key() {
+		return $this->key;
+	}
+
+	public function get_size() {
+		return $this->size;
+	}
+
+	public function get_etag() {
+		return $this->etag;
+	}
+
+	public function get_last_modified() {
+		return $this->last_modified;
+	}
+
+	public function to_array() {
+		return array(
+				'key' => $this->key,
+				'size' => $this->size,
+				'etag' => $this->etag,
+				'last_modified' => $this->last_modified
+		);
+	}
+}
+
+class GCUploadPart extends  GCObject {
+	protected $partnumber;// part number of this part
+	
+	/**Tags used in parse xml*/
+	public static $partnumberTag = "PartNumber";
+	public static $lastModifiedTag = "LastModified";
+	public static $etagTag = "ETag";
+	public static $sizeTag = "Size";
+	public static $keyTag = "Key";
+	public static $partTag = "Part";
+	
+    public function __construct($key, $size, $last_modified, $etag,$part_number) {
+    	parent::__construct($key, $size, $last_modified, $etag);
+    	$this->partnumber = $part_number;
+    }
+    
+    public function get_partnumber() {
+    	return $this->partnumber;
+    }
+    
+    public function to_array() {
+    	return array(
+    			GCUploadPart::$keyTag => $this->key,
+    			GCUploadPart::$partnumberTag => $this->partnumber,
+    			GCUploadPart::$lastModifiedTag => $this->last_modified,
+    			GCUploadPart::$etagTag => $this->etag,
+    			GCUploadPart::$sizeTag => $this->size
+    			);
+    }
+    
+    public function to_xml_for_completemultipartupload() {
+    	
+    	$xml = "<PartNumber>{$this->partnumber}</PartNumber>";
+        $xml .= "<ETag>{$this->etag}</ETag>";
+       
+        return $xml;
+    }
+}
+
+/**
+ * GCMUltipartUpload
+ * @author fun
+ *
+ */
+class GCMultipartUpload {
+	protected $bucket;// bucket's name
+	protected $key;//the object name of this Multipart Upload
+	protected $uploadid;//id used to identify multipartupload
+	protected $initiated; //when this upload initiated 
+
+	/**Tags used in parse xml body*/
+	public static $InitiateMultipartUploadResultTag = "InitiateMultipartUploadResult";
+	public static $bucketTag = "Bucket";
+	public static $keyTag = "Key";
+	public static $uploadIdTag = "UploadId";
+	public static $initiatedTag = "Initiated";
+
+	public function __construct($bucket,$key,$uploadid,$initiated = '') {
+		$this->bucket = $bucket;
+		$this->key = $key;
+		$this->uploadid = $uploadid;
+		$this->initiated = $initiated;
+	}
+
+
+	public function get_bucket() {
+		return $this->bucket;
+	}
+
+	public function get_key() {
+		return $this->key;
+	}
+
+	public function get_uploadid() {
+		return $this->uploadid;
+	}
+	
+	public function get_initated() {
+		return $this->initiated;
+	}
+
+	public function to_array() {
+		$meta_data =array(
+				GCMultipartUpload::$bucketTag => $this->bucket,
+				GCMultipartUpload::$keyTag => $this->key,
+				GCMultipartUpload::$uploadIdTag => $this->uploadid
+		);
+	    
+		if (! empty($this->initiated)) {
+			$meta_data[GCMultipartUpload::$initiatedTag] = $this->initiated;
+		}
+		return $meta_data;
+	}
+	
+}
+
+
+// Entity对象
+class GCEntity {
+	protected $bucket; // bucket名称
+	protected $prefix = ''; // 获取对象时前缀过滤字符串
+	protected $marker = ''; // 获取对象时偏移对象的名称
+	protected $maxkeys; // 获取对象时返回的最大记录数
+	protected $delimiter; // 获取对象时使用的分隔符
+	protected $istruncated = false; // 返回结果是否经过截短?
+	protected $objectarray = array(); // object list array
+
+	public function __construct() {
+		return $this;
+	}
+
+	public function set_bucket($bucket) {
+		$this->bucket = $bucket;
+	}
+
+	public function get_bucket() {
+		return $this->bucket;
+	}
+
+	public function set_prefix($prefix) {
+		$this->prefix = $prefix;
+	}
+
+	public function get_prefix() {
+		return $this->prefix;
+	}
+
+	public function set_marker($marker) {
+		$this->marker = $marker;
+	}
+
+	public function get_marker() {
+		return $this->marker;
+	}
+
+	public function set_maxkeys($maxkeys) {
+		$this->maxkeys = $maxkeys;
+	}
+
+	public function get_maxkeys() {
+		return $this->maxkeys;
+	}
+
+	public function set_delimiter($delimiter) {
+		$this->delimiter = $delimiter;
+	}
+
+	public function get_delimiter() {
+		return $this->delimiter;
+	}
+
+	public function set_istruncated($istruncated) {
+		$this->istruncated = $istruncated;
+	}
+
+	public function get_istruncated() {
+		return $this->istruncated;
+	}
+
+	public function add_object($object) {
+		$this->objectarray[]= $object;
+	}
+
+	public function get_object($idx=null) {
+		if ($idx === null) {
+			return $this->objectarray;
+		}
+
+		$max = count($this->objectarray);
+
+		$idx = intval($idx);
+		if ($idx < 0) {
+			$idx += $max;
+		}
+
+		if ($idx >= 0 && $idx < $max) {
+			return $this->objectarray[$idx];
+		}
+
+		return null;
+	}
+
+	public function to_array() {
+		return array(
+				'bucket' => $this->bucket,
+				'prefix' => $this->prefix,
+				'marker' => $this->marker,
+				'maxkeys' => $this->maxkeys,
+				'delimiter' => $this->delimiter,
+				'istruncated' => $this->istruncated,
+				'object' => $this->objectarray
+		);
+	}
+}
+
+class GCMultipartUploadEntity extends GCEntity {
+	protected $uploadidmarker = '';
+	protected $nextkeymarker = '';
+	protected $nextuploadidmarker = '';
+
+	/**Tags of List Multipart Uploads*/
+	public static $listMultipartUploadsResultTag = "ListMultipartUploadsResult";
+	public static $bucketTag = "Bucket";
+	public static $delimiterTag = "Delimiter";
+	public static $prefixTag = "Prefix";
+	public static $maxUploadsTag = "MaxUploads";
+	public static $keyMarkerTag = "KeyMarker";
+	public static $uploadIdMarker = "UploadIdMarker";
+	public static $nextKeyMarkerTag = "NextKeyMarker";
+	public static $nextUploadIdMarkerTag = "NextUploadIdMarker";
+	public static $isTruncatedTag = "IsTruncated";
+	public static $uploadTag = "Upload";
+	public static $commonPrefixesTag = "CommonPrefixes";
+
+	
+	public function __construct() {
+		parent::__construct();
+		return $this;
+	}
+	
+	public function set_maxUploads($maxuploads) {
+		parent::set_maxkeys($maxuploads);
+	}
+	
+	public function get_maxUploads() {
+		return parent::get_maxkeys();
+	}
+	
+	public function set_keyMarker( $keymarker ) {
+		parent::set_marker($keymarker);
+	}
+	
+	public function getKeyMarker() {
+		return parent::get_marker();
+	}
+	
+	public function set_uploadIdMarker($uploadIdMarker) {
+		$this->uploadidmarker = $uploadIdMarker;
+	}
+	
+	public function get_uploadIdMarker() {
+		return $this->uploadidmarker;
+	}
+	
+	public function set_nextKeyMarker( $nextKeyMarker ) {
+		$this->nextkeymarker = $nextKeyMarker;
+	}
+	
+	public function get_nextKeyMarker() {
+		return $this->nextkeymarker;
+	}
+	
+	public function set_nextUploadIdMarker( $nextuploadIdMarker ) {
+		$this->nextuploadidmarker = $nextuploadIdMarker;
+	}
+	
+	public function get_nextUploadIdMarker() {
+		return $this->nextuploadidmarker;
+	}
+	
+	public function addUpload( $upload ) {
+		parent::add_object($upload);
+	}
+	
+	public function get_upload($idx = null) {
+		return parent::get_object($idx);
+	}
+	
+	public function to_array(){
+		return array(
+				GCMultipartUploadEntity::$bucketTag => $this->bucket,
+				GCMultipartUploadEntity::$prefixTag => $this->prefix,
+				GCMultipartUploadEntity::$keyMarkerTag => $this->marker,
+				GCMultipartUploadEntity::$maxUploadsTag => $this->maxkeys,
+				GCMultipartUploadEntity::$delimiterTag => $this->delimiter,
+				GCMultipartUploadEntity::$uploadIdMarker => $this->uploadidmarker,
+				GCMultipartUploadEntity::$nextKeyMarkerTag => $this->nextkeymarker,
+				GCMultipartUploadEntity::$nextUploadIdMarkerTag => $this->nextuploadidmarker,
+				GCMultipartUploadEntity::$isTruncatedTag => $this->istruncated,
+				GCMultipartUploadEntity::$uploadTag => $this->objectarray
+		);
+	}
+}
+
+//Entity used in list parts
+class GCPartsEntity {
+	protected $bucket = ""; // bucket's name
+	protected $key = ""; // multipart upload's key
+	protected $uploadid = "";
+	protected $maxparts = ""; // the maximum number of parts returned in the response body
+	protected $istruncated = "";//$part_number_marker,the part to start with
+	protected $partnumbermarker = "";// part number to start with
+	protected $nextpartnumbermarker = "";// next part number to start with
+	protected $partsarray = array();
+	
+	/**Tags used in parse listpart result*/
+	public static $listpartsresultTag = "ListPartsResult";
+	public static $bucketTag = "Bucket";
+	public static $keyTag = "Key";
+	public static $uploadIdTag = "UploadId";
+	public static $maxpartsTag = "MaxParts";
+	public static $istruncatedTag = "IsTruncated";
+	public static $partnumberMarkerTag = "PartNumberMarker";
+	public static $nextpartnumbermarkerTag = "NextPartNumberMarker";
+	public static $partTag = "Part";
+	
+	public function __construct() {
+		return $this;
+	}
+	
+	public function set_bucket($bucket) {
+		$this->bucket = $bucket;
+	}
+	
+	public function get_bucket() {
+		return $this->bucket;
+	}
+	
+	public function set_key ($key) {
+		$this->key = $key;
+	}
+	
+	
+	public function get_key(){
+		return $this->key;
+	}
+	
+	public function set_uploadid($uploadid) {
+		$this->uploadid = $uploadid;
+	}
+	
+	
+	public function get_uploadid() {
+		return $this->uploadid;
+	}
+	
+	public function set_maxparts( $maxparts ) {
+	    $this->maxparts = $maxparts;	
+	}
+	
+	public function get_maxparts() {
+		return $this->maxparts;
+	}
+	
+	public function set_istruncated( $istruncated ) {
+	    	$this->istruncated = $istruncated;
+	}
+	
+	public function get_istruncated () {
+		return $this->istruncated;
+	}
+	
+	public function set_partnumbermarker( $partnumbermarker ){
+		$this->partnumbermarker = $partnumbermarker;
+	}
+	
+	public function get_partnumbermarker() {
+		return $this->partnumbermarker;
+	}
+	
+	public function set_nextpartnumbermarker( $nextpartnumbermarker ) {
+		$this->nextpartnumbermarker = $nextpartnumbermarker;
+	}
+	
+	public function get_nextpartnumbermarker(){
+		return $this->nextpartnumbermarker;
+	}
+	
+	public function add_part( $part ) {
+		$this->partsarray[] = $part;
+	}
+	
+	public function get_part($idx = null) {
+		if (null === $idx) {
+		   return $this->partsarray;
+		}
+		$max = count($this->partsarray);
+		
+		$idx = intval($idx);
+		if ($idx < 0) {
+			$idx += $max;
+		}
+		
+		if ($idx >= 0 && $idx < $max) {
+			return $this->partsarray[$idx];
+		}
+		
+		return null;
+	}
+	
+	public function to_array() {
+		return array(
+				GCPartsEntity::$bucketTag => $this->bucket,
+				GCPartsEntity::$keyTag => $this->key,
+				GCPartsEntity::$uploadIdTag => $this->uploadid,
+				GCPartsEntity::$maxpartsTag => $this->maxparts,
+				GCPartsEntity::$istruncatedTag => $this->istruncated,
+				GCPartsEntity::$partnumberMarkerTag => $this->partnumbermarker,
+				GCPartsEntity::$nextpartnumbermarkerTag => $this->nextpartnumbermarker,
+				GCPartsEntity::$partTag => $this->partsarray
+				);
+	}
+	
+	/**
+	 * build complete multipart upload xml
+	 */
+	public function to_completemultipartuploadxml() {
+		
+		$parts_xml = '<?xml version="1.0" encoding="UTF-8"?>';
+		$parts_xml .= "<CompleteMultipartUpload>";
+		foreach($this->partsarray as $part) {
+			$parts_xml .= "<Part>";
+			$parts_xml .= $part->to_xml_for_completemultipartupload();
+			$parts_xml .= "</Part>";
+		}
+		$parts_xml .= "</CompleteMultipartUpload>";
+		return $parts_xml;
+		
+	}
+	
+	
+}
+// MIME对象
+class GCMIME {
+	/**
+	 * MIME map of the file extensions.
+	 */
+	protected static $mime_maps = array(
+			'3gp' => 'video/3gpp',
+			'ai' => 'application/postscript',
+			'aif' => 'audio/x-aiff',
+			'aifc' => 'audio/x-aiff',
+			'aiff' => 'audio/x-aiff',
+			'asc' => 'text/plain',
+			'atom' => 'application/atom+xml',
+			'au' => 'audio/basic',
+			'avi' => 'video/x-msvideo',
+			'bcpio' => 'application/x-bcpio',
+			'bin' => 'application/octet-stream',
+			'bmp' => 'image/bmp',
+			'cdf' => 'application/x-netcdf',
+			'cgm' => 'image/cgm',
+			'class' => 'application/octet-stream',
+			'cpio' => 'application/x-cpio',
+			'cpt' => 'application/mac-compactpro',
+			'csh' => 'application/x-csh',
+			'css' => 'text/css',
+			'dcr' => 'application/x-director',
+			'dif' => 'video/x-dv',
+			'dir' => 'application/x-director',
+			'djv' => 'image/vnd.djvu',
+			'djvu' => 'image/vnd.djvu',
+			'dll' => 'application/octet-stream',
+			'dmg' => 'application/octet-stream',
+			'dms' => 'application/octet-stream',
+			'doc' => 'application/msword',
+			'dtd' => 'application/xml-dtd',
+			'dv' => 'video/x-dv',
+			'dvi' => 'application/x-dvi',
+			'dxr' => 'application/x-director',
+			'eps' => 'application/postscript',
+			'etx' => 'text/x-setext',
+			'exe' => 'application/octet-stream',
+			'ez' => 'application/andrew-inset',
+			'flv' => 'video/x-flv',
+			'gif' => 'image/gif',
+			'gram' => 'application/srgs',
+			'grxml' => 'application/srgs+xml',
+			'gtar' => 'application/x-gtar',
+			'gz' => 'application/x-gzip',
+			'hdf' => 'application/x-hdf',
+			'hqx' => 'application/mac-binhex40',
+			'htm' => 'text/html',
+			'html' => 'text/html',
+			'ice' => 'x-conference/x-cooltalk',
+			'ico' => 'image/x-icon',
+			'ics' => 'text/calendar',
+			'ief' => 'image/ief',
+			'ifb' => 'text/calendar',
+			'iges' => 'model/iges',
+			'igs' => 'model/iges',
+			'jnlp' => 'application/x-java-jnlp-file',
+			'jp2' => 'image/jp2',
+			'jpe' => 'image/jpeg',
+			'jpeg' => 'image/jpeg',
+			'jpg' => 'image/jpeg',
+			'js' => 'application/x-javascript',
+			'kar' => 'audio/midi',
+			'latex' => 'application/x-latex',
+			'lha' => 'application/octet-stream',
+			'lzh' => 'application/octet-stream',
+			'm3u' => 'audio/x-mpegurl',
+			'm4a' => 'audio/mp4a-latm',
+			'm4p' => 'audio/mp4a-latm',
+			'm4u' => 'video/vnd.mpegurl',
+			'm4v' => 'video/x-m4v',
+			'mac' => 'image/x-macpaint',
+			'man' => 'application/x-troff-man',
+			'mathml' => 'application/mathml+xml',
+			'me' => 'application/x-troff-me',
+			'mesh' => 'model/mesh',
+			'mid' => 'audio/midi',
+			'midi' => 'audio/midi',
+			'mif' => 'application/vnd.mif',
+			'mov' => 'video/quicktime',
+			'movie' => 'video/x-sgi-movie',
+			'mp2' => 'audio/mpeg',
+			'mp3' => 'audio/mpeg',
+			'mp4' => 'video/mp4',
+			'mpe' => 'video/mpeg',
+			'mpeg' => 'video/mpeg',
+			'mpg' => 'video/mpeg',
+			'mpga' => 'audio/mpeg',
+			'ms' => 'application/x-troff-ms',
+			'msh' => 'model/mesh',
+			'mxu' => 'video/vnd.mpegurl',
+			'nc' => 'application/x-netcdf',
+			'oda' => 'application/oda',
+			'ogg' => 'application/ogg',
+			'ogv' => 'video/ogv',
+			'pbm' => 'image/x-portable-bitmap',
+			'pct' => 'image/pict',
+			'pdb' => 'chemical/x-pdb',
+			'pdf' => 'application/pdf',
+			'pgm' => 'image/x-portable-graymap',
+			'pgn' => 'application/x-chess-pgn',
+			'pic' => 'image/pict',
+			'pict' => 'image/pict',
+			'png' => 'image/png',
+			'pnm' => 'image/x-portable-anymap',
+			'pnt' => 'image/x-macpaint',
+			'pntg' => 'image/x-macpaint',
+			'ppm' => 'image/x-portable-pixmap',
+			'ppt' => 'application/vnd.ms-powerpoint',
+			'ps' => 'application/postscript',
+			'qt' => 'video/quicktime',
+			'qti' => 'image/x-quicktime',
+			'qtif' => 'image/x-quicktime',
+			'ra' => 'audio/x-pn-realaudio',
+			'ram' => 'audio/x-pn-realaudio',
+			'ras' => 'image/x-cmu-raster',
+			'rdf' => 'application/rdf+xml',
+			'rgb' => 'image/x-rgb',
+			'rm' => 'application/vnd.rn-realmedia',
+			'roff' => 'application/x-troff',
+			'rtf' => 'text/rtf',
+			'rtx' => 'text/richtext',
+			'sgm' => 'text/sgml',
+			'sgml' => 'text/sgml',
+			'sh' => 'application/x-sh',
+			'shar' => 'application/x-shar',
+			'silo' => 'model/mesh',
+			'sit' => 'application/x-stuffit',
+			'skd' => 'application/x-koan',
+			'skm' => 'application/x-koan',
+			'skp' => 'application/x-koan',
+			'skt' => 'application/x-koan',
+			'smi' => 'application/smil',
+			'smil' => 'application/smil',
+			'snd' => 'audio/basic',
+			'so' => 'application/octet-stream',
+			'spl' => 'application/x-futuresplash',
+			'src' => 'application/x-wais-source',
+			'sv4cpio' => 'application/x-sv4cpio',
+			'sv4crc' => 'application/x-sv4crc',
+			'svg' => 'image/svg+xml',
+			'swf' => 'application/x-shockwave-flash',
+			't' => 'application/x-troff',
+			'tar' => 'application/x-tar',
+			'tcl' => 'application/x-tcl',
+			'tex' => 'application/x-tex',
+			'texi' => 'application/x-texinfo',
+			'texinfo' => 'application/x-texinfo',
+			'tif' => 'image/tiff',
+			'tiff' => 'image/tiff',
+			'tr' => 'application/x-troff',
+			'tsv' => 'text/tab-separated-values',
+			'txt' => 'text/plain',
+			'ustar' => 'application/x-ustar',
+			'vcd' => 'application/x-cdlink',
+			'vrml' => 'model/vrml',
+			'vxml' => 'application/voicexml+xml',
+			'wav' => 'audio/x-wav',
+			'wbmp' => 'image/vnd.wap.wbmp',
+			'wbxml' => 'application/vnd.wap.wbxml',
+			'webm' => 'video/webm',
+			'wml' => 'text/vnd.wap.wml',
+			'wmlc' => 'application/vnd.wap.wmlc',
+			'wmls' => 'text/vnd.wap.wmlscript',
+			'wmlsc' => 'application/vnd.wap.wmlscriptc',
+			'wmv' => 'video/x-ms-wmv',
+			'wrl' => 'model/vrml',
+			'xbm' => 'image/x-xbitmap',
+			'xht' => 'application/xhtml+xml',
+			'xhtml' => 'application/xhtml+xml',
+			'xls' => 'application/vnd.ms-excel',
+			'xml' => 'application/xml',
+			'xpm' => 'image/x-xpixmap',
+			'xsl' => 'application/xml',
+			'xslt' => 'application/xslt+xml',
+			'xul' => 'application/vnd.mozilla.xul+xml',
+			'xwd' => 'image/x-xwindowdump',
+			'xyz' => 'chemical/x-xyz',
+			'zip' => 'application/zip'
+			);
+
+			/**
+			 * Get file MIME according to its extension.
+			 *
+			 * @param string $ext
+			 * @return string
+			 */
+			public static function get_type($ext) {
+				return isset(self::$mime_maps[$ext]) ? self::$mime_maps[$ext] : 'application/octet-stream';
+			}
+}
+
+// Error对象
+class GCError extends Exception {
+	protected $requestId; // the request sign, used for error trace
+	protected $requestResource; // the request target resource
+	protected $errorCode; // error code
+	protected $errorMessage; // error message
+
+	public function __construct($response_code, $response_xml) {
+		if (!empty($response_xml)) {
+			$this->parse_errxml($response_xml);
+		}
+
+		parent::__construct($this->errorMessage, $response_code);
+	}
+
+	public function getId() {
+		return $this->requestId;
+	}
+
+	public function getResource() {
+		return $this->requestResource;
+	}
+
+	public function getErrorCode() {
+		return $this->errorCode;
+	}
+
+	public function getErrorMessage() {
+		return $this->errorMessage;
+	}
+
+	public function to_array() {
+		return array(
+				'code' => $this->code,
+				'message' => $this->message,
+				'errorCode' => $this->errorCode,
+				'errorMessage' => $this->errorMessage,
+				'requestId' => $this->requestId,
+				'requestResource' => $this->requestResource
+		);
+	}
+
+	protected function parse_errxml($error_xml) {
+		$error_xml = $this->get_xmlpart($error_xml);
+		$doc = new DOMDocument();
+		$doc->loadXML($error_xml);
+
+		$errorCode  = $doc->getElementsByTagName('Code')->item(0);
+		$this->errorCode = empty($errorCode) ? 'UnknownErrorCode' : $errorCode->nodeValue;
+
+		$errorMessage = $doc->getElementsByTagName('Message')->item(0);
+		$this->errorMessage = empty($errorMessage) ? 'UnknownErrorMessage' : $errorMessage->nodeValue;
+
+		$requestId = $doc->getElementsByTagName('RequestId')->item(0);
+		$this->requestId = empty($requestId) ? 'UnknownRequestId' : $requestId->nodeValue;
+
+		$requestResource = $doc->getElementsByTagName('Resource')->item(0);
+		$this->requestResource = empty($requestResource) ? 'UnknownRequestResource' : $requestResource->nodeValue;
+	}
+	
+	/**
+	 * Get xml part from response body
+	 */
+	protected function get_xmlpart($response_body) {
+       $tmparray = explode("\r\n\r\n", $response_body);
+       $realbody = array();	
+       for($i=0;$i<count($tmparray);$i++) {
+       	$tmp = trim($tmparray[$i]);
+       	//printf("\nvc".substr($tmp,0,strlen("<?xml"))."\n");
+       	if(substr($tmp,0,strlen("<?xml")) === "<?xml") {
+       		break;
+       	}
+       }
+       for(;$i<count($tmparray);$i++) {
+       	 $realbody[]=$tmparray[$i];
+       }
+       
+       $realxml = implode("\r\n\r\n",$realbody);
+      // printf("realxml:\n".$realxml."\n");
+       return $realxml;
+	}
+}
+
+class GrandCloudStorage {
+	/**
+	 * GrandCloud domain
+	 * @access protected
+	 */
+	protected $host;
+
+	/**
+	 * access_key
+	 * @access protected
+	 */
+	protected $access_key;
+
+	/**
+	 * access_secret
+	 * @access protected
+	 */
+	protected $access_secret;
+
+	/**
+	 * bucket name
+	 * @access protected
+	 */
+	protected $bucket;
+
+	/**
+	 * bucket cname
+	 * this is used for public access control
+	 *
+	 * @access protected
+	 */
+	protected $bucket_cname;
+
+	/**
+	 * http headers, array
+	 * @access protected
+	 */
+	protected $headers;
+
+	/**
+	 * http body, string
+	 * @access protected
+	 */
+	protected $body;
+
+	/**
+	 * http response code
+	 * @access protected
+	 */
+	protected $response_code;
+
+	/**
+	 * http response header
+	 * @access protected
+	 */
+	protected $response_header;
+
+	/**
+	 * http response content length
+	 * @access protected
+	 */
+	protected $response_length;
+
+	/**
+	 * http response content text
+	 * @access protected
+	 */
+	protected $response_body;
+
+	/**
+	 * last curl error
+	 * @access protected
+	 */
+	protected $last_curl_error;
+
+	/**
+	 * debug switch
+	 * @access protected
+	 */
+	protected $debug = false;
+	
+	
+	/**
+	 * default location
+	 */
+	const DEFAULT_LOCATION = 'huadong-1';
+	
+   
+
+	/**
+	 * constructor
+	 * @param string $host  storage host, no ending slash
+	 * @param string $bucket  default bucket
+	 * @return $this object
+	 */
+	public function __construct($host='', $bucket=null) {
+		$this->host = $host;
+		$this->bucket = $bucket;
+
+		return $this;
+	}
+
+	/**
+	 * set region host
+	 * @param string $host
+	 * @return $this object
+	 */
+	public function set_host($host) {
+		$this->host = $host;
+
+		return $this;
+	}
+
+	/**
+	 * get current region host
+	 * @param void
+	 * @return string
+	 */
+	public function get_host() {
+		return $this->host;
+	}
+
+	/**
+	 * set access_key and access_secret
+	 * @param string $access_key
+	 * @param string $access_secret
+	 * @return $this object
+	 */
+	public function set_key_secret($access_key, $access_secret) {
+		$this->access_key = $access_key;
+		$this->access_secret = $access_secret;
+
+		return $this;
+	}
+
+	/**
+	 * get current access_key
+	 * @param void
+	 * @return string
+	 */
+	public function get_access_key() {
+		return $this->access_key;
+	}
+
+	/**
+	 * get current access_secret
+	 * @param void
+	 * @return string
+	 */
+	public function get_access_secret() {
+		return $this->access_secret;
+	}
+
+	/**
+	 * set debug switch
+	 * @param bool $flag  true/false
+	 * @return $this object
+	 */
+	public function set_debug($flag) {
+		$this->debug = ($flag === true);
+
+		return $this;
+	}
+
+	/**
+	 * set default bucket
+	 * @param string $name  bucket's name
+	 * @param [opt] string $cname  bucket's cname
+	 * @return $this object
+	 */
+	public function set_bucket($name, $cname=null) {
+		$this->bucket = $name;
+
+		if (!empty($cname)) {
+			$this->bucket_cname = $cname;
+		}
+
+		return $this;
+	}
+
+	/**
+	 * get current bucket
+	 * @param void
+	 * @return string
+	 */
+	public function get_bucket() {
+		return $this->bucket;
+	}
+
+	/**
+	 * set default bucket cname
+	 * @param string $cname  bucket's cname
+	 * @return $this object
+	 */
+	public function set_bucket_cname($cname) {
+		$this->bucket_cname = $cname;
+
+		return $this;
+	}
+
+	/**
+	 * get current bucket cname
+	 * @param void
+	 * @return string
+	 */
+	public function get_bucket_cname() {
+		return $this->bucket_cname;
+	}
+
+	/**
+	 * Set http request header fields
+	 * @param string $field  http header field
+	 * @param string $value  value of the field
+	 * usually $field is a string without ":",and $value is not empty,
+	 * example:$filed = "mykey1",$value = "myvalue1";
+	 * meanwhile,$field can be like "key1:value1\nkey2:value2\n..",
+	 * and $value will unused in this situation.
+	 * 
+	 * @return $this object
+	 */
+	public function set_header($field, $value=null) {
+		$field = trim($field);
+		$value = trim($value);
+
+		if (empty($field)) {
+			return $this;
+		}
+
+		if (strpos($field, ':')) {  //$field can be like "key1:value1\nkey2:value2\n..",$value will unused in this situation 
+			foreach (explode("\n", $field) as $item) {
+				$key = substr($item, 0, strpos($item, ':'));
+
+				$this->headers[$key] = $item;
+			}
+		} else {
+			$this->headers[$field] = "{$field}: {$value}";
+		}
+
+		return $this;
+	}
+
+	/**
+	 * Remove http header field
+	 * @param string $field
+	 * @return $this object
+	 */
+	public function remove_header($field) {
+		$field = trim($field);
+		if (isset($this->headers[$field])) {
+			unset($this->headers[$field]);
+		}
+
+		return $this;
+	}
+
+	/**
+	 * Set http request body
+	 * @param string $content  http request body
+	 * @return $this object
+	 */
+	public function set_body($content) {
+		$this->body = $content;
+
+		return $this;
+	}
+
+	/**
+	 * Get response code
+	 * @param void
+	 * @return integer
+	 */
+	public function get_response_code() {
+		return $this->response_code;
+	}
+
+	/**
+	 * get response header
+	 * @param void
+	 * @return string
+	 */
+	public function get_response_header() {
+		return $this->response_header;
+	}
+
+	/**
+	 * Get response content length
+	 * @param void
+	 * @return integer
+	 */
+	public function get_response_length() {
+		return $this->response_length;
+	}
+
+	/**
+	 * Get response content
+	 * @param void
+	 * @return string
+	 */
+	public function get_response_body() {
+		return $this->response_body;
+	}
+
+	/**
+	 * Get last curl error message
+	 * @param void
+	 * @return string
+	 */
+	public function get_curl_error() {
+		return $this->last_curl_error;
+	}
+
+	/**
+	 * Get all buckets,corresponds to "GET Service" in API
+	 * @param void
+	 * @return GCBucket objects list
+	 * @exception see GCError
+	 */
+	public function get_allbuckets() {
+		//$conn = $this->make_request('GET', '/');
+		$conn = $this->make_request_with_path_and_params_split("GET",'/');
+		$code = $this->exec_request($conn);
+
+		if (200 != $code) {
+			throw new Exception($this->response_body, $code);
+		}
+
+		return $this->parse_bucketsxml($this->response_body);
+	}
+
+	/**
+	 * Get bucket's metas, now only return idc,
+	 * corresponds to "GET Bucket Location" in API
+	 * @param string $bucket  bucket's name
+	 * @return string  bucket's idc info
+	 * @exception see GCError
+	 */
+	public function head_bucket($bucket) {
+		$bucket = trim($bucket, '/');
+		$bucket = "/{$bucket}?location";
+
+		//$conn = $this->make_request('GET', $bucket);
+		$conn = $this->make_request_with_path_and_params_split("GET",$bucket);
+		$code = $this->exec_request($conn);
+
+		if (200 != $code) {
+			throw new Exception($this->response_body, $code);
+		}
+
+		return $this->parse_localxml($this->response_body);
+	}
+
+	/**
+	 * Create new bucket,corresponds to "PUT Bucket" in API
+	 * @param string $name  bucket's name to create
+	 * @param string $local  bucket's region, region of your bucket,
+	 * region currently support "huadong-1", "huabei-1", default to huabei-1
+	 * @return true on success
+	 * @exception see GCError
+	 */
+	public function put_bucket($name, $local='huabei-1') {
+		$local_xml = $this->make_bucket_local($local);
+		$this->set_header('Content-Length', strlen($local_xml));
+		$this->set_body($local_xml);
+
+		//$conn = $this->make_request('PUT', $name, '', 'text/xml');
+		$conn = $this->make_request_with_path_and_params_split("PUT",$name,array(),'','text/xml');
+		$code = $this->exec_request($conn);
+
+		// code: 204 = success
+		if (204 != $code) {
+			throw new Exception($this->response_body, $code);
+		}
+
+		return true;
+	}
+
+	/**
+	 * Delete specified bucket,corresponds to "Delete Bucket" in API
+	 * @param string $name  bucket's name to delete
+	 * @return true on success
+	 * @exception throw exception when bucket is not empty or response invalid
+	 */
+	public function delete_bucket($name) {
+		$this->set_header('Content-Length', 0);
+       
+		//$conn = $this->make_request('DELETE', $name);
+		$conn = $this->make_request_with_path_and_params_split("DELETE",$name);
+		$code = $this->exec_request($conn);
+
+		// code: 204 = success
+		if (204 != $code) {
+			throw new Exception($this->response_body, $code);
+		}
+		return true;
+	}
+
+	/**
+	 * Get bucket policy,corresponds to "GET Bucket Policy" in API
+	 * @param string $bucket  bucket's name
+	 * @return string  bucket's policy json
+	 * @exception see GCError
+	 */
+	public function get_bucket_policy($name) {
+		$bucket = trim($name,'/');
+		$path = "/{$bucket}?policy";
+
+		$conn = $this->make_request_with_path_and_params_split("GET", $path);
+		$code = $this->exec_request($conn);
+
+		if (200 != $code) {
+			throw new Exception($this->response_body, $code);
+		}
+
+		return $this->response_body;
+	}
+
+	/**
+	 * Put bucket policy to storage,corresponds to "PUT Bucket Policy" in API
+	 * @param string $bucket  bucket's name
+	 * @param array $policy  bucket policy config,if $policy is null,delete old bucket policy
+	 * @return true on success
+	 * @exception see GCError
+	 */
+	public function put_bucket_policy($bucket, $policy) {
+		
+		if($policy === null) {  // if policy is null,delete bucket policy
+			$is_old_policy = true;
+			try {
+				$old_policy = $this->get_bucket_policy($bucket);
+			} catch (Exception $e) {
+				if ($e->getErrorCode() != ERR_BUCKET_NOPOLICY) {
+					throw $e;
+				}
+			
+				$is_old_policy = false;
+			}
+			if(!$is_old_policy) {  // 之前服务器上并不存在bucket policy,直接返回
+				return true;
+			}
+
+			return $this->delete_bucket_policy($bucket);
+		}
+
+		$policy_object = array(
+				'Id' => $this->make_uuid(),
+				'Statement' => $policy
+		);
+
+		$stream = fopen('data://text/plain,' . rawurlencode(json_encode($policy_object)), 'rb');
+         
+		$code = $this->post_or_put_request("PUT", "{$bucket}?policy", $stream);
+		if($code !== 204) {
+			throw new Exception($this->response_body,$code);
+		}
+		
+		return true;
+	}
+
+	/**
+	 * Delete bucket policy,corresponds to "DELETE Bucket Policy" in API
+	 * @param $bucket bucket's name
+	 * @return true on success
+	 * @exception see GCError
+	 */
+	public function delete_bucket_policy($bucket) {
+		$path = "{$bucket}?policy";
+
+		return $this->delete_bucket($path);
+	}
+
+	/**
+	 * Get all objects of specified bucket,corresponds to "GET Bucket" in API
+	 * @param string $bucket  bucket's name
+	 * @param integer $maxkeys  max response objects number of per-request
+	 * @param string $marker  response objects offset
+	 * @param string $delimiter  response objects name filter
+	 * @param string $prefix  response objects name filter
+	 * @return GCEntity object
+	 * @exception see GCError
+	 */
+	public function get_allobjects($bucket, $maxkeys=null, $marker='', $delimiter='', $prefix='') {
+		$bucket = trim($bucket, '/');
+		$bucket = "/{$bucket}";
+		$params = array();
+		if (!empty($maxkeys)) {
+			$maxkeys = intval($maxkeys);
+			if ($maxkeys > 0) {
+				$params['max-keys'] = $maxkeys;
+			}
+		}
+
+		if ($marker !== '') {
+			$params['marker'] = trim($marker);
+		}
+		
+
+		if ($delimiter !== '') {
+			$params['delimiter'] = trim($delimiter);
+		}
+
+		if ($prefix !== '') {
+			$params['prefix'] = trim($prefix);
+		}
+         
+        $conn = $this->make_request_with_path_and_params_split("GET",$bucket,$params);
+		$code = $this->exec_request($conn);
+
+		if (200 != $code) {
+			throw new Exception($this->response_body, $code);
+		}
+
+		return $this->parse_objectsxml($this->response_body);
+	}
+
+
+	/**
+	 * Get all multipart (corresponds to "List Multipart Upload" in API)
+	 * @param string $bucket, your bucketname
+	 * @param string $key_marker,the key to start with
+	 * @param string $upload_id_marker,the uploadid to start with
+	 * @param int $max_uploads,the maximum number of keys returned in the response body
+	 * @param string $prefix,the prefix parameter to the key of the multipart upload you want to retrive
+	 * @param char $delimiter,the param you use to group keys
+	 * @return GCMultipartUploadEntity object on success
+	 * @exception throw exception when response invalid
+	 */
+	public function get_all_multipart_upload($bucket,$key_marker='',$upload_id_marker='', $max_uploads=null, $prefix='', $delimiter='') {
+		$bucket = trim($bucket, '/');
+		$bucket = "/{$bucket}";
+		
+		$params = array();
+		if ($max_uploads !== null) {
+			$max_uploads = intval($max_uploads);
+			if ($max_uploads > 0) {
+				$params['max-uploads'] = $max_uploads;
+			}
+		}
+		
+		if ('' !== trim($key_marker)) {
+			$params['key-marker'] = trim($key_marker);
+		}
+		
+		if('' !== trim($upload_id_marker)) {
+			$params['upload-id-marker'] = trim($upload_id_marker);
+		}
+		if ('' !== trim($delimiter)) {
+			$params['delimiter'] = trim($delimiter);
+		}
+		
+		if ('' !== trim($prefix)) {
+			$params['prefix'] = trim($prefix);
+		}
+
+		$path = $bucket.'?uploads';		
+		$conn = $this->make_request_with_path_and_params_split('GET',$path,$params);		
+		$code = $this->exec_request($conn);
+		
+		if (200 != $code) {
+			throw new Exception($this->response_body, $code);
+		}
+		
+		return $this->parse_multipart_uploadsxml($this->response_body);
+	}
+	/**
+	 * Get object's metas(corresponds to "HEAD Object" in API)
+	 * @param string $name  object's name
+	 * @return array('name'=>'?', 'meta'=>array(...), 'size'=>?) when success
+	 * @exception see GCError
+	 */
+	public function head_object($name) {
+		$conn = $this->make_request('HEAD', $name);
+		$code = $this->exec_request($conn);
+
+		if (200 != $code) {
+			throw new Exception($this->response_body, $code);
+		}
+        $result = $this->parse_header($this->response_header);
+        $result['name'] = $name;
+        $result['size'] = $this->response_length;
+		return $result;
+	}
+
+	/**
+	 * Put object to storage(corresponds to "PUT Object" in API)
+	 * @param string $name  object's name
+	 * @param string $source  local file path(/path/to/filename.ext) or stream
+	 * @param string $content_meta  see make_request()
+	 * @param string $content_type  see make_request()
+	 * @param string $content_md5  see make_request()
+	 * @return true on success
+	 * @exception see GCError
+	 */
+	public function put_object($name, $source, $content_meta='', $content_type='', $content_md5='') {
+		if (is_resource($source)) { // stream upload
+			if (empty($name)) {
+				throw new Exception('$name must be supplied for resource type!', 500);
+			}
+            fseek($source,0,0);
+		}
+		elseif (is_string($source)) { // file upload			
+			if (empty($name)) {
+				$name = basename($source);
+			}
+		}
+        
+        if(empty($content_type)) {
+			$pathinfo = pathinfo($name);
+			$content_type = GCMIME::get_type(isset($pathinfo['extension']) ? $pathinfo['extension'] : '');
+		}
+		$code = $this->post_or_put_request("PUT", $name, $source,array(),$content_meta,$content_type,$content_md5);
+		// code: 204 = success
+		if (204 != $code) {
+			throw new Exception($this->response_body, $code);
+		}
+		return true;
+		
+	}
+    
+    /**
+     * Copy Object(corresponds to "PUT Object - Copy" in API)
+     * @param $sbucket,name of source bucket
+     * @param $skey,name of source object
+     * @param $dbucket,name of destnation bucket
+     * @param $dkey,name of destnation object
+     * @param $content_meta,fileds will be sended as request headers, 
+     *           like x-snda-meta-XXXX or those headers do not necessary 
+     * return new object info on success
+     * @exception see GCError
+     */
+    public function copy_object($sbucket,$skey,$dbucket,$dkey,$content_meta="",$content_type="") {
+    	$path = "/{$dbucket}/{$dkey}";
+    	$copy_source = "/{$sbucket}/{$skey}";
+    	return $this->copy_from_path_to_path($copy_source,$path,$content_meta,$content_type);
+    }
+    
+    /**
+     * Copy from source path to destnation path, now it used in copy object and upload part copy
+     * @param $from_path,source path
+     * @param $to_path,destnation path
+     * @param $content_meta,fileds will be sended as request headers, 
+     *           like x-snda-meta-XXXX or those headers do not necessary 
+     * return request info on success
+     * @exception see GCError
+     */
+    public function copy_from_path_to_path($from_path,$to_path,$content_meta="",$content_type="") {
+    	$content_meta = trim($content_meta);
+    	if(!empty($content_meta)) {
+    		$content_meta .= ",";
+    	}
+    	$content_meta .= "x-snda-copy-source:{$from_path}";
+    	$conn = $this->make_request_with_path_and_params_split("PUT",$to_path,array(),$content_meta,$content_type);
+    	$code = $this->exec_request($conn);
+    	if($code != 200) {
+    		throw Exception($this->response_body,$code);
+    	} 
+    	return $this->parse_copy_object_result($this->response_body);
+    }
+    
+	/**
+	 * Get object from storage(corresponds to "GET Object" in API)
+	 * @param string $name  object's name
+	 * @param string $target  write to local file path(/path/to/filename.ext) or stream
+	 * @param boolean $auto_close  if auto close the $target passed when it is a stream?
+	 * @return true on success
+	 * @exception see GCError
+	 */
+	public function get_object($name, $target=null, $auto_close=false) {
+		$this->head_object($name);//why? ask spring
+
+		//$conn = $this->make_request('GET', $name);
+        $conn = $this->make_request_with_path_and_params_split("GET",$name);
+        
+		$is_stream = false;
+		if ($target !== null) {
+			if (is_resource($target)) { // write to stream
+				$is_stream = true;
+
+				$target_stream = $target;
+			}
+			else if (is_string($target)) { // write to local file
+				$target_stream = fopen($target, 'wb');
+				if (!$target_stream) {
+					curl_close($conn);
+
+					throw new Exception("Unable to open {$target}", 500);
+				}
+			}
+
+			if ($target_stream) {
+				curl_setopt_array($conn, array(
+						CURLOPT_HEADER    => false,
+						CURLOPT_FILE      => $target_stream
+				));
+			}
+		}
+
+		$code = $this->exec_request($conn, true);
+
+		if ($auto_close && $is_stream) {
+			fclose($target_stream);
+		}
+
+		if (200 != $code) {
+			throw new Exception($this->response_body, $code);
+		}
+
+		return true;
+	}
+
+	/**
+	 * Get object resource from storage
+	 * @param string $name  object's name
+	 * @param integer $expire  expire of resource
+	 * @return resource on success
+	 */
+	public function get_object_resource($name, $expire=300) {
+		$this->head_object($name);
+
+		$path = $this->get_abs_path($name);
+		$expire = time() + $expire;
+
+		$auth = "GET\n"                // HTTP Method
+		."\n"                   // Content-MD5 Field
+		."\n"                   // Content-Type Field
+		."{$expire}\n"          // Date Field
+		.''                     // Canonicalized SNDA Headers
+		.$path;                 // Filepath
+
+		$req_cname = $this->get_bucket_cname();
+		$req_params = http_build_query(array(
+				'SNDAAccessKeyId' => $this->get_access_key(),
+				'Expires' => $expire,
+				'Signature' => base64_encode(hash_hmac('sha1', $auth, $this->access_secret, true))
+		));
+
+		return "{$req_cname}{$path}?{$req_params}";
+	}
+
+	/**
+	 * Delete object from storage (corresponds to "Delete Object" in API)
+	 * @param string $name  object's name
+	 * @return true on success
+	 * @exception see GCError
+	 */
+	public function delete_object($name) {
+		//$conn = $this->make_request('DELETE', $name);
+		$conn = $this->make_request_with_path_and_params_split("DELETE",$name);
+		$code = $this->exec_request($conn);
+
+		// code: 204 = success
+		if (204 != $code) {
+			throw new Exception($this->response_body, $code);
+		}
+
+		return true;
+	}
+
+	/**
+	 * Initiate multipart upload
+	 * (corresponds to "Initiate multipart upload" in API)
+	 * @param string $bucket,bucket's name
+	 * @param string $key, object's name
+	 * @param string $content_meta  see make_request()
+	 * @param string $content_type  see make_request()
+	 * @param string $content_md5  see make_request()
+	 * @return $array,multipart upload info on success
+	 * @exception throw exception when response invalid
+	 */
+	public function initiate_multipart_upload($bucket, $key, $content_meta='', $content_type='') {
+		 
+		$bucket = trim($bucket,"/");
+		$path = "/{$bucket}/{$key}?uploads";
+		if(empty($key)) {
+			$pathinfo = pathinfo($key);
+			$content_type = GCMIME::get_type(isset($pathinfo['extension']) ? $pathinfo['extension'] : '');
+
+		}
+		if(empty($content_type)) {
+			$pathinfo = pathinfo($key);
+			$content_type = GCMIME::get_type(isset($pathinfo['extension']) ? $pathinfo['extension'] : '');
+		}
+		 
+		//$conn = $this->make_request("POST", $path,$content_meta,$content_type);
+		$conn = $this->make_request_with_path_and_params_split("POST",$path,array(),$content_meta,$content_type);
+		$code = $this->exec_request($conn);
+		 
+		if(200 != $code)  {
+			throw new Exception($this->response_body,$code);
+		}
+		return $this->parse_initiate_multipart_upload_response($this->response_body);
+	}
+	
+
+     
+	/**
+	 * Upload part to storage
+	 * @param string $bucketname bucket's name
+	 * @param string $key  object's name
+	 * @param string $uploadid multipart upload's id
+	 * @param int $partnumber of this part
+	 * @param string $source  local file path(/path/to/filename.ext) or stream
+	 * @param long $contentlength the length of this content
+	 * @param string $content_md5  see make_request()
+	 * @return true on success
+	 * @exception throw exception when failed
+	 */
+	public function upload_part($bucketname, $key, $uploadid,$partnumber,$source, $contentlength = null,  $content_md5='') {
+		
+		if("" === $bucketname || "" === $key || (! is_numeric($partnumber))){
+			throw new Exception('Illegal params');
+		}
+		$params = array(
+				"partNumber" => $partnumber,
+				"uploadId" => $uploadid
+				);
+		$path = "/{$bucketname}/{$key}?".http_build_query($params);
+		
+		$code = $this->post_or_put_request("PUT", $path, $source,array(),'','',$content_md5,$contentlength);
+		
+		if($code != 204) {
+			throw new Exception($this->response_body, $code);
+		}
+		return true;
+	}
+	
+
+    /**
+     * Upload Part - Copy (corresponds to "Upload Part - Copy" in API)
+     * @param $sbucket,name of source bucket
+     * @param $skey,name of source object
+     * @param $dbucket,name of destnation bucket
+     * @param $dkey,name of destnation object
+     * @param $uploadid,uploadid of the multipart upload
+     * @param $partnumber,partnumber of the part to create
+     * @param $content_meta,fileds will be sended as request headers, 
+     *           like x-snda-meta-XXXX or those headers do not necessary 
+     * return part info on success
+     * @exception see GCError
+     */
+    public function upload_part_copy($sbucket,$skey,$dbucket,$dkey,$uploadid,$partnumber,$content_meta="") {
+		$params = array(
+				"partNumber" => $partnumber,
+				"uploadId" => $uploadid
+				);
+		$path = "/{$dbucket}/{$dkey}?".http_build_query($params);
+		$copy_source = "/{$sbucket}/{$skey}";
+        return $this->copy_from_path_to_path($copy_source,$path,$content_meta);
+	}
+	
+	/**
+	 * Abort multipart upload
+	 * @param string $bucket,bucket's name
+	 * @param string $key,object's name
+	 * @param string $uploadId, the uploadid of the multipart upload
+	 * @throws Exception when failed
+	 * @true on success
+	 */
+	public function abortMultipartUpload($bucket,$key,$uploadId){
+		$path = "/{$bucket}/{$key}?uploadId={$uploadId}";
+		$conn = $this->make_request_with_path_and_params_split('DELETE', $path);
+		$code = $this->exec_request($conn);
+		
+		// code: 204 = success
+		if (204 != $code) {
+			throw new Exception($this->response_body, $code);
+		}
+		
+		return true;
+	}
+
+	
+	/**
+	 * list parts
+	 * @param string,$bucket,bucket's name
+	 * @param string,$key,object's name
+	 * @param string $uploadId,the uploadid of the multipart upload
+	 * @param int $max_parts, the maximum number of parts returned in the response body
+	 * @param string $part_number_marker,the part to start with
+	 * @return GCPartsEntity on success
+	 * @exception,throw exception when failed
+	 */
+	public function list_parts($bucket,$key,$uploadId,$max_parts = null,$part_number_marker = '') {
+		if("" === $bucket || "" === $key || "" === $uploadId){
+			throw new Exception('Illegal params');
+		}
+		$bucket = trim($bucket);
+		$params = array();
+		$path = "/{$bucket}/{$key}?uploadId={$uploadId}";
+		if(is_numeric($max_parts)) {
+			$params["max-parts"] = $max_parts;
+		}
+		if(is_numeric($part_number_marker)) {
+			$params["part-number-marker"] = $part_number_marker;
+		}
+		$conn = $this->make_request_with_path_and_params_split("GET", $path,$params);
+		$code = $this->exec_request($conn);
+		if (200 != $code) {
+			throw new Exception($this->response_body, $code);
+		}
+		
+		return $this->parse_listspartxml($this->response_body);
+		
+	}
+	
+	/**
+	 * complete multipartupload
+	 * @param string $bucket
+	 * @param string $key
+	 * @param string $uploadid
+	 * @param string $complete_xml,if is '',then we will get it by list_parts($uploadid) 
+	 * @throws Exception when failed
+	 * @return response body
+	 */
+	public function complete_multipartupload($bucket,$key,$uploadid,$complete_xml = ''){
+		if(empty($complete_xml)) {
+			try {
+				$partEntity = $this->list_parts($bucket, $key, $uploadid);
+				$complete_xml = $partEntity->to_completemultipartuploadxml();
+			} catch(Exception $e) {
+				throw $e;
+			}
+		}
+		
+		$path = "/{$bucket}/{$key}?uploadId={$uploadid}";
+		$stream = fopen('data://text/plain,' . rawurlencode($complete_xml), 'rb');
+		$code = $this->post_or_put_request("POST",$path,$stream);
+		
+		if ( 300 <= $code) {
+			throw new Exception($this->response_body, $code);
+		}
+		return $this->parse_complete_multipart_uploadxml($this->response_body);
+	}
+	
+	/**
+	 * get resource abs path
+	 * @param string $path
+	 * @return string
+	 */
+	public function get_abs_path($path) {
+		if ('/' != $path[0]) {
+			$path = $this->bucket ? "/{$this->bucket}/{$path}" : "/{$path}";
+		}
+
+		$path = preg_replace('~/+~', '/', $path);
+
+		return $path;
+	}
+
+
+	/**
+	 * Execute curl request
+	 * @param resource $ch  curl handle
+	 * @param bool $close_request  whether call curl_close() after execute request
+	 * @return http success status code or false
+	 * @exception throw GCError when response code in 400~499 range
+	 */
+	public function exec_request($ch, $close_request=true) {
+		if (!is_resource($ch)) {
+			return false;
+		}
+
+		$response = curl_exec($ch);
+		$this->last_curl_error = curl_error($ch);
+		if (!empty($this->last_curl_error)) {
+			throw new Exception($this->last_curl_error,0);
+		}
+
+		$this->response_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+		$this->response_length = curl_getinfo($ch, CURLINFO_CONTENT_LENGTH_DOWNLOAD);
+		$tmparray = explode("\r\n\r\n", $response);
+		if (isset($tmparray[1])) {
+			$this->response_header = array_shift($tmparray);
+			$this->response_body = implode("\r\n\r\n", $tmparray);
+		} else {
+			$this->response_body = $response;
+		}
+
+		if ($close_request) {
+			curl_close($ch);
+		}
+
+		if ($this->response_code >= 400 && $this->response_code <= 499) {
+			throw new GCError($this->response_code, $this->response_body);
+		}
+
+		return $this->response_code;
+	}
+
+	public function parse_header($header) {
+		$tmparray = explode("\r\n", $header);
+
+		$result = array();
+		$others = array();
+		foreach ($tmparray as $item) {
+			$item = trim($item);
+			if ('x-snda-meta-' === substr($item, 0, 12)) {
+				$tmpitem = explode(':', $item);
+				if (isset($tmpitem[1])) {
+					$result[substr(trim($tmpitem[0]), 12)] = trim($tmpitem[1]);
+				} else {
+					$result[substr($item, 12)] = null;
+				} 
+			} else if('HTTP' === substr($item,0,strlen('HTTP'))) {
+				continue;
+			}else {
+				$tmpitem = explode(':', $item);
+				if(isset($tmpitem[1])) {
+					$others[trim($tmpitem[0])] = trim($tmpitem[1]);
+				} else {
+					$others[trim($tmpitem[0])] = null;
+				}
+			}
+		}
+
+        $others['meta'] = $result;
+		return $others;
+	}
+
+    
+	public function parse_bucketsxml($bucketsxml) {
+		
+		$bucketsxml = $this->get_xmlpart($bucketsxml);
+		$doc = new DOMDocument();
+		$doc->loadXML($bucketsxml);
+
+		$buckets = $doc->getElementsByTagName('Bucket');
+
+		$bucketsarray = array();
+		foreach($buckets as $xml) {
+			$idc = self::DEFAULT_LOCATION;
+			$name = $xml->getElementsByTagName('Name')->item(0)->nodeValue;
+			$ctime = $xml->getElementsByTagName('CreationDate')->item(0)->nodeValue;
+
+			$local = $xml->getElementsByTagName('Location')->item(0);
+			if ($local && !empty($local->nodeValue)) {
+				$idc = $local->nodeValue;
+			}
+
+			$bucketsarray[] = new GCBucket($idc, $name, $ctime);
+		}
+
+		return $bucketsarray;
+	}
+
+	public function parse_localxml($idcxml) {
+		$idcxml = $this->get_xmlpart($idcxml);
+		$doc = new DOMDocument();
+		$doc->loadXML($idcxml);
+
+		$local  = $doc->getElementsByTagName('LocationConstraint')->item(0);
+		if (empty($local)) {
+			return self::DEFAULT_LOCATION;
+		}
+
+		if (!empty($local->nodeValue)) {
+			return $local->nodeValue;
+		}
+
+		return self::DEFAULT_LOCATION;
+	}
+
+    protected function parse_copy_object_result($body) {
+    	$body_xml = $this->get_xmlpart($body);
+    	$doc = new DOMDocument();
+    	$doc->loadXML($body_xml);
+    	$message = $doc->getElementsByTagName("CopyObjectResult");
+    	if(empty($message)) {
+    		throw new GCError($body,200);
+    	} 
+    	try{
+	    	$result = array(
+	    	"LastModified" =>  $doc->getElementsByTagName("LastModified")->item(0)->nodeValue,
+	    	"ETag" =>  $doc->getElementsByTagName("ETag")->item(0)->nodeValue);
+	    	return $result;
+    	}catch(Exception $e ) {
+    		throw new Exception($body,200);
+    	}
+    }
+    
+	public function parse_objectsxml($objectxml) {
+		$objectxml = $this->get_xmlpart($objectxml);
+		$doc = new DOMDocument();
+		$doc->loadXML($objectxml);
+
+		$xpath = new DOMXPath($doc);
+
+		$entity = new GCEntity();
+
+		$name = $xpath->query('/ListBucketResult/Name')->item(0);
+		if ($name) {
+			$entity->set_bucket($name->nodeValue);
+		}
+
+		$maxkeys = $xpath->query('/ListBucketResult/MaxKeys')->item(0);
+		if ($maxkeys) {
+			$entity->set_maxkeys($maxkeys->nodeValue);
+		}
+
+		$istruncated = $xpath->query('/ListBucketResult/IsTruncated')->item(0);
+		if ($istruncated) {
+			$entity->set_istruncated($istruncated->nodeValue === 'true');
+		}
+
+		$prefix = $xpath->query('/ListBucketResult/Prefix')->item(0);
+		if ($prefix) {
+			$entity->set_prefix($prefix->nodeValue);
+		}
+
+		$delimiter = $xpath->query('/ListBucketResult/Delimiter')->item(0);
+		if ($delimiter) {
+			$entity->set_delimiter($delimiter->nodeValue);
+		}
+
+		$marker = $xpath->query('/ListBucketResult/NextMarker')->item(0);
+		if ($marker) {
+			$entity->set_marker($marker->nodeValue);
+		}
+
+		$objects = $xpath->query('/ListBucketResult/Contents');
+		foreach($objects as $xml) {
+			$key           = $xml->getElementsByTagName('Key')->item(0)->nodeValue;
+			$size          = $xml->getElementsByTagName('Size')->item(0)->nodeValue;
+			$lastmodified  = $xml->getElementsByTagName('LastModified')->item(0)->nodeValue;
+			$etag          = $xml->getElementsByTagName('ETag')->item(0)->nodeValue;
+
+			$entity->add_object(new GCObject($key, $size, $lastmodified, $etag));
+		}
+
+		$common = $xpath->query('/ListBucketResult/CommonPrefixes');
+		foreach ($common as $comxml) {
+			$folders = $comxml->getElementsByTagName('Prefix');
+			foreach ($folders as $xml) {
+				$entity->add_object(new GCObject($xml->nodeValue, '-', '-', ''));
+			}
+		}
+
+		// adjust marker when no delimiter
+		if ($marker === null && $entity->get_istruncated()) {
+			$last_object = $entity->get_object(-1);
+            if(empty($last_object) === false) {
+			   $entity->set_marker($last_object->get_key());
+            }
+		}
+
+		return $entity;
+	}
+
+    
+	protected function parse_initiate_multipart_upload_response($response_xml) {
+		$response_xml = $this->get_xmlpart($response_xml);
+		$doc = new DOMDocument();
+		$doc->loadXML($response_xml);
+		$multipart_upload_tree = $doc->getElementsByTagName(GCMultipartUpload::$InitiateMultipartUploadResultTag)->item(0);
+		if($multipart_upload_tree->childNodes->length) {
+			$bucket = $multipart_upload_tree->getElementsByTagName(GCMultipartUpload::$bucketTag)->item(0)->nodeValue;
+			$key = $multipart_upload_tree->getElementsByTagName(GCMultipartUpload::$keyTag)->item(0)->nodeValue;
+			$value = $multipart_upload_tree -> getElementsByTagName(GCMultipartUpload::$uploadIdTag)->item(0)->nodeValue;
+			$result = new GCMultipartUpload($bucket,$key,$value);
+			return $result->to_array();
+		}
+		return false;
+	}
+    
+	protected function parse_multipart_uploadsxml($mulpart_uploads_xml) {
+		$mulpart_uploads_xml = $this->get_xmlpart($mulpart_uploads_xml);
+		$doc = new DOMDocument();
+		$doc->loadXML($mulpart_uploads_xml);
+
+		$xpath = new DOMXPath($doc);
+
+		$entity = new GCMultipartUploadEntity();
+
+		$bucketTag = '/'.GCMultipartUploadEntity::$listMultipartUploadsResultTag.'/'.GCMultipartUploadEntity::$bucketTag;
+		$bucket = $xpath->query($bucketTag)->item(0);
+		if ($bucket) {
+			$entity->set_bucket($bucket->nodeValue);
+		}
+
+		$maxkeysTag = '/'.GCMultipartUploadEntity::$listMultipartUploadsResultTag.'/'.GCMultipartUploadEntity::$maxUploadsTag;
+		$maxkeys = $xpath->query($maxkeysTag)->item(0);
+		if ($maxkeys) {
+			$entity->set_maxUploads($maxkeys->nodeValue);
+		}
+
+		$istruncatedTag = '/'.GCMultipartUploadEntity::$listMultipartUploadsResultTag.'/'.GCMultipartUploadEntity::$isTruncatedTag;
+		$istruncated = $xpath->query($istruncatedTag)->item(0);
+		if ($istruncated) {
+			$entity->set_istruncated($istruncated->nodeValue === 'true');
+		}
+
+		$prefixTag = '/'.GCMultipartUploadEntity::$listMultipartUploadsResultTag.'/'.GCMultipartUploadEntity::$prefixTag;
+		$prefix = $xpath->query($prefixTag)->item(0);
+		if ($prefix) {
+			$entity->set_prefix($prefix->nodeValue);
+		}
+
+		$delimiterTag = '/'.GCMultipartUploadEntity::$listMultipartUploadsResultTag.'/'.GCMultipartUploadEntity::$delimiterTag;
+		$delimiter = $xpath->query($delimiterTag)->item(0);
+		if ($delimiter) {
+			$entity->set_delimiter($delimiter->nodeValue);
+		}
+
+		$keymarkerTag = '/'.GCMultipartUploadEntity::$listMultipartUploadsResultTag.'/'.GCMultipartUploadEntity::$keyMarkerTag;
+		$marker = $xpath->query($keymarkerTag)->item(0);
+		if ($marker) {
+			$entity->set_keyMarker($marker->nodeValue);
+		}
+
+		$uploadidmarkerTag = '/'.GCMultipartUploadEntity::$listMultipartUploadsResultTag.'/'.GCMultipartUploadEntity::$uploadIdMarker;
+		$uploadidmarker = $xpath->query($uploadidmarkerTag)->item(0);
+		if($uploadidmarker) {
+			$entity->set_uploadIdMarker($uploadidmarker->nodeValue);
+		}
+		
+		$nextkeymarkerTag = '/'.GCMultipartUploadEntity::$listMultipartUploadsResultTag.'/'.GCMultipartUploadEntity::$nextKeyMarkerTag;
+		$nextkeymarker = $xpath->query($nextkeymarkerTag)->item(0);
+		if($nextkeymarker) {
+			$entity -> set_nextKeyMarker($nextkeymarker->nodeValue);
+		}
+		
+		$nextuploadidmarkerTag = '/'.GCMultipartUploadEntity::$listMultipartUploadsResultTag.'/'.GCMultipartUploadEntity::$nextUploadIdMarkerTag;
+		$nextuploadidmarker = $xpath -> query($nextuploadidmarkerTag) -> item(0);
+		if($nextuploadidmarker) {
+			$entity -> set_nextUploadIdMarker($nextuploadidmarker->nodeValue);
+		}
+		
+		$uploadTag = '/'.GCMultipartUploadEntity::$listMultipartUploadsResultTag.'/'.GCMultipartUploadEntity::$uploadTag;
+		$uploads = $xpath->query($uploadTag);
+		foreach($uploads as $xml) {
+			$key           = $xml->getElementsByTagName(GCMultipartUpload::$keyTag)->item(0)->nodeValue;
+			$uploadid          = $xml->getElementsByTagName(GCMultipartUpload::$uploadIdTag)->item(0)->nodeValue;
+			$lastmodified  = $xml->getElementsByTagName(GCMultipartUpload::$initiatedTag)->item(0)->nodeValue;
+			$entity->addUpload(new GCMultipartUpload($bucket->nodeValue,$key,$uploadid,$lastmodified));
+		}
+
+		$commonPrefixTag = '/'.GCMultipartUploadEntity::$listMultipartUploadsResultTag.'/'.GCMultipartUploadEntity::$commonPrefixesTag;
+		$common = $xpath->query($commonPrefixTag);
+		foreach ($common as $comxml) {
+			$folders = $comxml->getElementsByTagName(GCMultipartUploadEntity::$prefixTag);
+			foreach ($folders as $xml) {
+				$entity->add_object(new GCMultipartUpload($bucket->nodeValue, $xml->nodeValue, '-', '-'));
+			}
+		}
+
+		return $entity;
+	}
+	
+	protected function parse_listspartxml($listparts_xml) {
+		$listparts_xml = $this->get_xmlpart($listparts_xml);
+		$doc = new DOMDocument();
+		$doc->loadXML($listparts_xml);
+	
+		$xpath = new DOMXPath($doc);
+	
+		$entity = new GCPartsEntity();
+	
+		$bucketTag = '/'.GCPartsEntity::$listpartsresultTag.'/'.GCPartsEntity::$bucketTag;
+		$bucket = $xpath->query($bucketTag)->item(0);
+		if ($bucket) {
+			$entity->set_bucket($bucket->nodeValue);
+		}
+	
+		$keyTag =  '/'.GCPartsEntity::$listpartsresultTag.'/'.GCPartsEntity::$keyTag;
+		$key = $xpath->query($keyTag)->item(0);
+		if ($key) {
+			$entity->set_key($key->nodeValue);
+		}
+	     
+		$uploadidTag =  '/'.GCPartsEntity::$listpartsresultTag.'/'.GCPartsEntity::$uploadIdTag;
+		$uploadid = $xpath->query($uploadidTag)->item(0);
+		if($uploadid) {
+		   $entity->set_uploadid($uploadid->nodeValue);	
+		}
+		
+		$maxpartsTag =  '/'.GCPartsEntity::$listpartsresultTag.'/'.GCPartsEntity::$maxpartsTag;
+		$maxparts = $xpath->query($maxpartsTag)->item(0);
+		if($maxparts) {
+			$entity->set_maxparts($maxparts->nodeType);
+		}
+		
+		$istruncatedTag =  '/'.GCPartsEntity::$listpartsresultTag.'/'.GCPartsEntity::$istruncatedTag;
+        $istruncated = $xpath->query($istruncatedTag)->item(0);
+		if ($istruncated) {
+			$entity->set_istruncated($istruncated->nodeValue === 'true');
+		}
+	
+		$partnumbermarkerTag =  '/'.GCPartsEntity::$listpartsresultTag.'/'.GCPartsEntity::$partnumberMarkerTag;
+		$partnumbermarker = $xpath->query($partnumbermarkerTag)->item(0);
+		if($partnumbermarker) {
+			$entity->set_partnumbermarker($partnumbermarker->nodeValue);
+		}
+		
+		$nextpartnumbermarkerTag =  '/'.GCPartsEntity::$listpartsresultTag.'/'.GCPartsEntity::$nextpartnumbermarkerTag;
+		$nextpartnumbermarker = $xpath->query($nextpartnumbermarkerTag)->item(0);
+		if($nextpartnumbermarker) {
+			$entity->set_nextpartnumbermarker($nextpartnumbermarker->nodeValue);
+		}
+		
+
+		$partsTag =  '/'.GCPartsEntity::$listpartsresultTag.'/'.GCPartsEntity::$partTag;
+		$parts = $xpath->query($partsTag);
+		foreach($parts as $xml) {
+			$partnumber           = $xml->getElementsByTagName(GCUploadPart::$partnumberTag)->item(0)->nodeValue;
+			$etag          = $xml->getElementsByTagName(GCUploadPart::$etagTag)->item(0)->nodeValue;
+			$lastmodified  = $xml->getElementsByTagName(GCUploadPart::$lastModifiedTag)->item(0)->nodeValue;
+			$size 		   = $xml->getElementsByTagName(GCUploadPart::$sizeTag)->item(0)->nodeValue;
+			$entity->add_part(new GCUploadPart($key->nodeValue,$size,$lastmodified,$etag,$partnumber));
+		}
+	
+		return $entity;
+	}
+	
+	protected function parse_complete_multipart_uploadxml($xml) {
+		 $xml = $this->get_xmlpart($xml);
+		 $completeMultipartUploadResultTag = "CompleteMultipartUploadResult";
+		 $locationTag = "/".$completeMultipartUploadResultTag."/Location";
+		 $bucketTag = "/".$completeMultipartUploadResultTag."/Bucket";
+		 $keyTag = "/".$completeMultipartUploadResultTag."/Key";
+		 $eTagTag = "/".$completeMultipartUploadResultTag."/ETag";
+		 
+		 $doc = new DOMDocument();
+		 $doc->loadXML($xml);
+		 
+		 $result = array();
+		 $xpath = new DOMXPath($doc);
+		 $location = $xpath->query($locationTag)->item(0);
+		 if($location) {
+		 	$result["Location"] = $location->nodeValue;
+		 }
+		 
+		 $bucket = $xpath->query($bucketTag)->item(0);
+		 if($bucket) {
+		 	$result["Bucket"] = $bucket->nodeValue;
+		 }
+		 
+		 $key = $xpath->query($keyTag)->item(0);
+		 if($key) {
+		 	$result["Key"] = $key->nodeValue;
+		 }
+		 
+		 $eTag = $xpath->query($eTagTag)->item(0);
+		 if($eTag) {
+		 	$result["ETag"] = $eTag->nodeValue;
+		 }
+		 
+		 return $result;
+	}
+	
+	/**
+	 * Get xml part from response body
+	 */
+	protected function get_xmlpart($response_body) {
+       $tmparray = explode("\r\n\r\n", $response_body);
+       $realbody = array();	
+       for($i=0;$i<count($tmparray);$i++) {
+       	$tmp = trim($tmparray[$i]);
+       	//printf("\nvc".substr($tmp,0,strlen("<?xml"))."\n");
+       	if(substr($tmp,0,strlen("<?xml")) === "<?xml") {
+       		break;
+       	}
+       }
+       for(;$i<count($tmparray);$i++) {
+       	 $realbody[]=$tmparray[$i];
+       }
+       
+       $realxml = implode("\r\n\r\n",$realbody);
+      // printf("realxml:\n".$realxml."\n");
+       return $realxml;
+	}
+	/**
+	 * generate uuid string
+	 * @param string $prefix
+	 * @return string
+	 */
+	protected function make_uuid($prefix='') {
+		$chars = md5(uniqid(mt_rand(), true));
+		$uuid = substr($chars,0,8) . '-';
+		$uuid .= substr($chars,8,4) . '-';
+		$uuid .= substr($chars,12,4) . '-';
+		$uuid .= substr($chars,16,4) . '-';
+		$uuid .= substr($chars,20,12);
+
+		return $prefix . $uuid;
+	}
+     
+	/**
+	 * sign the data
+	 * @param string $data
+	 * @return string
+	 */
+	protected function make_sign($data) {
+		return 'SNDA'.' '.$this->access_key.':'.base64_encode(hash_hmac('sha1', $data, $this->access_secret, true));
+	}
+
+	/**
+	 * adjust the meta
+	 * @param string $meta
+	 * @return string
+	 */
+	protected function make_meta($meta) {
+		/**
+		 * compress
+		 * x-snda-meta-row: abc, x-snda-meta-row: bcd
+		 * to
+		 * x-snda-meta-row:abc,bcd  // value have no lead space
+		 */
+		$tmparray = array();
+		foreach (explode(',', trim($meta)) as $item) {
+			$item = explode(':', $item);
+
+			if (isset($item[1])) {
+				$tmparray[trim($item[0])][] = trim($item[1]);
+			}
+		}
+
+		$keys = array_keys($tmparray);
+		sort($keys);
+
+		$meta = '';
+		foreach ($keys as $key) {
+			$meta .= "{$key}:".join(',', $tmparray[$key])."\n";
+		}
+
+		return $meta;
+	}
+
+	
+	/**
+	 * Generate request handler
+	 * @param string $method           GET, HEAD, PUT, DELETE
+	 * @param string $path             resource $path,used in sign
+	 * @param array $params            $query params
+	 * @param string $content_meta     x-snda-meta-XXXX field
+	 * @param string $content_type     Content-Type field
+	 * @param string $content_md5      Content-MD5 field
+	 * @return cURL handle on success, false if any error.
+	 */
+	protected function make_request_with_path_and_params_split($method, $path, $query_params = array(),$content_meta='', $content_type='', $content_md5='') {
+		$path = $this->get_abs_path($path);
+		if ($content_meta) {
+			$content_meta = $this->make_meta($content_meta);
+		
+			$this->set_header($content_meta);
+		}
+		
+		if ($content_type) {
+			$this->set_header('Content-Type', $content_type);
+		}
+		
+		if ($content_md5) {
+			$this->set_header('Content-MD5', base64_encode($content_md5));
+		}
+		
+		$conn = curl_init();
+		if ($conn) {
+			$url = "{$this->host}{$path}";
+			if(!empty($query_params)) {
+				$params_str = http_build_query($query_params);
+				if(false === strstr($path, '?')) {
+					$url .= "?";
+				} else {
+					$url .= "&";
+				}
+			   $url .= $params_str;
+			}
+			$date = date('r');
+			$auth = "{$method}\n"          // HTTP Method
+			."{$content_md5}\n"     // Content-MD5 Field
+			."{$content_type}\n"    // Content-Type Field
+			."{$date}\n"            // Date Field
+			.$content_meta          // Canonicalized SNDA Headers
+			.$path;                 // resource path
+		    //print_r("string to sign:".$auth."\n");
+			$this->set_header('Date', $date);
+			$this->set_header('Authorization', $this->make_sign($auth));
+			$this->set_header('Expect', '');
+			 
+			curl_setopt_array($conn, array(
+					CURLOPT_URL             => $url,
+					CURLOPT_VERBOSE         => $this->debug,
+					CURLOPT_CUSTOMREQUEST   => $method,
+					CURLOPT_CONNECTTIMEOUT  => 10,
+					CURLOPT_FOLLOWLOCATION  => true,
+					CURLOPT_HEADER          => true,
+					CURLOPT_NOBODY          => 'HEAD' === $method,
+					CURLOPT_RETURNTRANSFER  => true,
+					CURLOPT_BINARYTRANSFER  => true,
+					CURLOPT_HTTPHEADER      => $this->headers
+					));
+		
+					if (strstr($this->host, ':')) {
+						$tmparray = explode(':', $this->host);
+						if (isset($tmparray[2])) {
+						  curl_setopt($conn, CURLOPT_PORT, intval($tmparray[2]));
+			            }
+		            }
+		
+			if (!empty($this->body)) {
+				if (is_array($this->body)) {
+				 $this->body = http_build_query($this->body);
+				}
+			
+				curl_setopt_array($conn, array(
+				CURLOPT_POST          => 1,
+				CURLOPT_POSTFIELDS    => $this->body
+				));
+			}
+
+			$this->body = null;
+			$this->headers = array();
+		} else {
+			throw new Exception('Failed to init curl, maybe it is not supported yet?');
+		}
+		
+		return $conn;
+	}
+  
+	/**
+	 * generate request handler
+	 * @param string $method           GET, HEAD, PUT, DELETE
+	 * @param string $path             resource $path
+	 * @param string $content_meta     fileds will be sended as request headers, 
+	 *                            like x-snda-meta-XXXX or those headers do not necessary 
+	 * @param string $content_type     Content-Type field
+	 * @param string $content_md5      Content-MD5 field
+	 * @return cURL handle on success, false if any error.
+	 */
+    protected function make_request($method, $path,$content_meta='', $content_type='', $content_md5='') {
+        //$path = $this->get_abs_path($path);
+
+        $params = array();
+        if (strstr($path, '?')) {
+            $tmparray = explode('?', $path);
+
+            $path = array_shift($tmparray);
+            $query_string = implode('?', $tmparray);
+            parse_str($query_string,$params);
+        }
+        
+        $params_array = array();
+        if (!empty($params)) {
+        	$isfirst = true;
+        	
+        	foreach ($params as $key=>$value) {
+        		if($value === NULL || $value === '') {
+        			if(true === $isfirst) {
+        				$path .= "?".$key; // may have bug?
+        				$isfirst = false;
+        			} 
+        		} else {
+        			$params_array[$key] = $value;
+        		}
+        	}
+        }
+        return $this->make_request_with_path_and_params_split($method, $path, $params_array,$content_meta, $content_type, $content_md5);
+      
+    }
+	
+    /**
+     * process post or put request
+     * @param string $path,path need to sign
+     * @param resource $source,source data to post
+     * @param array $query_params,params in query
+     * @param string $content_meta,fileds will be sended as request headers, 
+	 *               like x-snda-meta-XXXX or those headers do not necessary.
+     * @param string $content_type
+     * @param string $content_md5
+     * @throws Exception
+     * @return code;
+     */
+    protected function post_or_put_request($method,$path, $source, $query_params=array(),$content_meta='', $content_type='', $content_md5='',$content_length ='') {
+    	
+    	
+    	if (is_resource($source)) { // stream upload
+    		$source_stream = $source;   	
+    		$source_fstat = fstat($source);
+    		$source_size = isset($source_fstat['size']) ? $source_fstat['size'] : 0;
+    		
+    	}
+    	elseif (is_string($source)) { // file upload
+    		clearstatcache();
+    		if (!is_file($source)) {
+    			throw new Exception("{$source} doesn't exist", 404);
+    		}
+    	
+    		$source_stream = fopen($source, 'rb');
+    		if (!$source_stream) {
+    			throw new Exception("Unable to read {$source}", 500);
+    		}  	
+    		$source_size = filesize($source);
+    	}
+    	elseif ($source === null) { // no content
+    		$source_stream = null;
+    	    $source_size = 0;  
+    	} else {
+    		throw new Exception('Unsupported source type!', 500);
+    	}
+    	
+    	if(is_numeric($content_length)) {   //if $content_length is set,check weather $content_length is illegal
+    		$content_length = intval($content_length);
+    		if($content_length > $source_size || $content_length < 0) {
+    			throw new Exception("Content_length({$content_length}) is illegal",500);
+    		} else {
+    			$source_size = $content_length;
+    		}
+    	}
+    	
+    	$code = 200;
+    	try {
+    		if ($source_size === 0) {
+    			$this->set_header('content-length', $source_size);
+    		}
+    	
+    		$conn = $this->make_request_with_path_and_params_split($method, $path,$query_params,$content_meta, $content_type, $content_md5);    	
+    		if ($source_size !== 0) {
+    			curl_setopt_array($conn, array(
+    					CURLOPT_PUT         => true,
+    					CURLOPT_INFILE      => $source_stream,
+    					CURLOPT_INFILESIZE  => $source_size
+    			));
+    		}
+    	
+    		$code = $this->exec_request($conn);
+    		if (is_resource($source_stream)) {
+    			fclose($source_stream);
+    		}
+    	
+    	} catch (Exception $e){
+    		if (is_resource($source_stream)) {
+    			fclose($source_stream);
+    		}
+    	
+    		throw $e;
+    	}
+    	
+    	return $code;
+    }
+
+	/**
+	 * build bucket local xml
+	 * @param string $local
+	 * @return string
+	 */
+	protected function make_bucket_local($local) {
+		$local = strtolower($local);
+		$template = '<?xml version="1.0" encoding="UTF-8"?><CreateBucketConfiguration><LocationConstraint>%s</LocationConstraint></CreateBucketConfiguration>';
+
+		//若用户选择huadong-1,则默认不传location body
+		$local_xml = '';
+		$local = trim($local);
+		switch ($local) {
+			case 'huadong-1':
+			    break;    
+			default:
+			    $local_xml = sprintf($template,$local);
+				break;
+		}
+		return $local_xml;
+	}
+
+}

+ 22 - 0
include/storages/oss/conf.inc.php

@@ -0,0 +1,22 @@
+<?php
+//个人测试
+//ACCESS_ID
+define('OSS_ACCESS_ID', defined('CS_AK')?CS_AK:'');
+
+//ACCESS_KEY
+define('OSS_ACCESS_KEY', defined('CS_SK')?CS_SK:'');
+
+
+//是否记录日志
+define('ALI_LOG', FALSE);
+
+//自定义日志路径,如果没有设置,则使用系统默认路径,在./logs/
+define('ALI_LOG_PATH','./');
+
+//是否显示LOG输出
+define('ALI_DISPLAY_LOG', FALSE);
+
+//语言版本设置
+define('ALI_LANG', 'zh');
+
+

+ 96 - 0
include/storages/oss/lang/zh.inc.php

@@ -0,0 +1,96 @@
+<?php
+/*%*************************************************************************************%*/
+//access id & access key 相关
+define('NOT_SET_OSS_ACCESS_ID', '未设置OSS服务的ACCESS ID');
+define('NOT_SET_OSS_ACCESS_KEY', '未设置OSS服务的ACCESS KEY');
+define('NOT_SET_OSS_ACCESS_ID_AND_ACCESS_KEY', '没有设置ACCESS ID & ACCESS KEY');
+define('OSS_ACCESS_ID_OR_ACCESS_KEY_EMPTY', 'ACCESS ID或ACCESS KEY为空');
+
+/*%*************************************************************************************%*/
+//OSS语言包以及文件相关
+define('OSS_LANG_FILE_NOT_EXIST', 'OSS语言包文件不存在');
+define('OSS_CONFIG_FILE_NOT_EXIST',OSS_API_PATH.DIRECTORY_SEPARATOR.'conf.inc.php不存在');
+define('OSS_UTILS_FILE_NOT_EXIST',OSS_API_PATH.DIRECTORY_SEPARATOR.'util'.DIRECTORY_SEPARATOR.'utils.php不存在');
+define('OSS_CURL_EXTENSION_MUST_BE_LOAD','系统没有安装CURL扩展');
+define('OSS_NO_ANY_EXTENSIONS_LOADED','系统没有安装任何扩展,请检查系统配置');
+
+
+/*%*************************************************************************************%*/
+//日志文件相关
+define('OSS_WRITE_LOG_TO_FILE_FAILED','日志写入失败,请检查日志文件是否存在或者日志文件的权限');
+define('OSS_LOG_PATH_NOT_EXIST','日志路径不存在');
+
+/*%**************************************************************************************%*/
+//OSS bucket操作相关
+define('OSS_OPTIONS_MUST_BE_ARRAY', '$option必须为数组');
+define('OSS_GET_BUCKET_LIST_SUCCESS','获取Bucket列表成功!');
+define('OSS_GET_BUCKET_LIST_FAILED', '获取Bucket列表失败!');
+define('OSS_CREATE_BUCKET_SUCCESS', '创建Bucket成功');
+define('OSS_CREATE_BUCKET_FAILED', '创建Bucket失败');
+define('OSS_DELETE_BUCKET_SUCCESS', '删除Bucket成功');
+define('OSS_DELETE_BUCKET_FAILED', '删除Bucket失败');
+define('OSS_BUCKET_NAME_INVALID', '未通过Bucket名称规则校验');
+define('OSS_GET_BUCKET_ACL_SUCCESS','获取Bucket ACL成功');
+define('OSS_GET_BUCKET_ACL_FAILED','获取Bucket ACL失败');
+define('OSS_SET_BUCKET_ACL_SUCCESS','设置Bucket ACL成功');
+define('OSS_SET_BUCKET_ACL_FAILED','设置Bucket ACL失败');
+define('OSS_ACL_INVALID','ACL不在允许范围,目前仅允许(private,public-read,public-read-write三种权限)');
+define('OSS_BUCKET_IS_NOT_ALLOWED_EMPTY', 'Bucket不允许为空');
+
+/*%****************************************************************************************%*/
+//OSS object操作相关
+define('OSS_GET_OBJECT_LIST_SUCCESS','获得OBJECT列表成功');
+define('OSS_GET_OBJECT_LIST_FAILED','获得OBJECT列表失败');
+define('OSS_CREATE_OBJECT_DIR_SUCCESS','创建OBJECT目录成功');
+define('OSS_CREATE_OBJECT_DIR_FAILED','创建OBJECT目录失败');
+define('OSS_DELETE_OBJECT_SUCCESS','删除OBJECT成功');
+define('OSS_DELETE_OBJECT_FAILED','删除OBJECT失败');
+define('OSS_UPLOAD_FILE_BY_CONTENT_SUCCESS','通过Http Body上传文件成功');
+define('OSS_UPLOAD_FILE_BY_CONTENT_FAILED','通过Http Body上传文件失败');
+define('OSS_GET_OBJECT_META_SUCCESS','获得OBJECT META成功');
+define('OSS_GET_OBJECT_META_FAILED','获得OBJECT META失败');
+define('OSS_OBJECT_NAME_INVALID','未通过Object名称规则校验');
+define('OSS_OBJECT_IS_NOT_ALLOWED_EMPTY','Object不允许为空');
+define('OSS_INVALID_HTTP_BODY_CONTENT','Http Body的内容非法');
+define('OSS_GET_OBJECT_SUCCESS','获得Object成功');
+define('OSS_GET_OBJECT_FAILED','获得Object失败');
+define('OSS_OBJECT_EXIST','Object存在');
+define('OSS_OBJECT_NOT_EXIST','Object不存在');
+define('OSS_NOT_SET_HTTP_CONTENT','为设置Http Body');
+define('OSS_INVALID_CONTENT_LENGTH','非法的Content-Length值');
+define('OSS_CONTENT_LENGTH_MUST_MORE_THAN_ZERO','Content-Length必须大于0');
+define('OSS_UPLOAD_FILE_NOT_EXIST','上传文件不存在');
+define('OSS_COPY_OBJECT_SUCCESS','拷贝Object成功');
+define('OSS_COPY_OBJECT_FAILED', '拷贝Object失败');
+define('OSS_FILE_NOT_EXIST','文件不存在');
+define('OSS_FILE_PATH_IS_NOT_ALLOWED_EMPTY', '上传文件路径为空');
+
+/*%****************************************************************************************%*/
+//OSS object Group操作相关
+define('OSS_CREATE_OBJECT_GROUP_SUCCESS','创建Object Group成功');
+define('OSS_CREATE_OBJECT_GROUP_FAILED','创建Object Group失败');
+define('OSS_GET_OBJECT_GROUP_SUCCESS','获取Object Group成功');
+define('OSS_GET_OBJECT_GROUP_FAILED','获取Object Group失败');
+define('OSS_GET_OBJECT_GROUP_INDEX_SUCCESS','获取Object Group Index成功');
+define('OSS_GET_OBJECT_GROUP_INDEX_FAILED','获取Object Group Index失败');
+define('OSS_GET_OBJECT_GROUP_META_SUCCESS','获取Object Group Group Meta成功');
+define('OSS_GET_OBJECT_GROUP_META_FAILED','获取Object Group Group Meta失败');
+define('OSS_DELETE_OBJECT_GROUP_SUCCESS','删除Object Group Group成功');
+define('OSS_DELETE_OBJECT_GROUP_FAILED','删除Object Group Group失败');
+define('OSS_OBJECT_GROUP_IS_NOT_ALLOWED_EMPTY', 'Object Group不允许为空');
+define('OSS_OBJECT_ARRAY_IS_EMPTY','创建Object Group的Object不允许为空');
+define('OSS_OBJECT_GROUP_TOO_MANY_OBJECT','每个Object Group最多包含1000个Object');
+
+/*%****************************************************************************************%*/
+//OSS Multi-Part Upload相关
+define('OSS_INITIATE_MULTI_PART_SUCCESS', '初始化Multi-Part Upload成功');
+define('OSS_INITIATE_MULTI_PART_FAILED', '初始化Multi-Part Upload失败');
+
+/*%*******************************************************************************************%*/
+//其他
+define('OSS_INVALID_OPTION_HEADERS', 'OPTIONS不是数组');
+
+
+
+
+

+ 25 - 0
include/storages/oss/lib/requestcore/LICENSE

@@ -0,0 +1,25 @@
+Copyright (c) 2006-2010 Ryan Parman, Foleeo Inc., and contributors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are
+permitted provided that the following conditions are met:
+
+	* Redistributions of source code must retain the above copyright notice, this list of
+	  conditions and the following disclaimer.
+
+	* Redistributions in binary form must reproduce the above copyright notice, this list
+	  of conditions and the following disclaimer in the documentation and/or other materials
+	  provided with the distribution.
+
+	* Neither the name of Ryan Parman, Foleeo Inc. nor the names of its contributors may be used to
+	  endorse or promote products derived from this software without specific prior written
+	  permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
+OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
+AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.

+ 15 - 0
include/storages/oss/lib/requestcore/README.md

@@ -0,0 +1,15 @@
+# RequestCore
+
+RequestCore is a lightweight cURL-based HTTP request/response class that leverages MultiCurl for parallel requests.
+
+### PEAR HTTP_Request?
+
+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.
+
+### Reference and Download
+
+You can find the class reference at <http://skyzyx.github.com/requestcore/>. You can get the code from <http://github.com/skyzyx/requestcore>.
+
+### License and Copyright
+
+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).

+ 1030 - 0
include/storages/oss/lib/requestcore/requestcore.class.php

@@ -0,0 +1,1030 @@
+<?php
+/**
+ * Handles all HTTP requests using cURL and manages the responses.
+ *
+ * @version 2011.06.07
+ * @copyright 2006-2011 Ryan Parman
+ * @copyright 2006-2010 Foleeo Inc.
+ * @copyright 2010-2011 Amazon.com, Inc. or its affiliates.
+ * @copyright 2008-2011 Contributors
+ * @license http://opensource.org/licenses/bsd-license.php Simplified BSD License
+ */
+class RequestCore
+{
+	/**
+	 * The URL being requested.
+	 */
+	public $request_url;
+
+	/**
+	 * The headers being sent in the request.
+	 */
+	public $request_headers;
+
+	/**
+	 * The body being sent in the request.
+	 */
+	public $request_body;
+
+	/**
+	 * The response returned by the request.
+	 */
+	public $response;
+
+	/**
+	 * The headers returned by the request.
+	 */
+	public $response_headers;
+
+	/**
+	 * The body returned by the request.
+	 */
+	public $response_body;
+
+	/**
+	 * The HTTP status code returned by the request.
+	 */
+	public $response_code;
+
+	/**
+	 * Additional response data.
+	 */
+	public $response_info;
+
+	/**
+	 * The handle for the cURL object.
+	 */
+	public $curl_handle;
+
+	/**
+	 * The method by which the request is being made.
+	 */
+	public $method;
+
+	/**
+	 * Stores the proxy settings to use for the request.
+	 */
+	public $proxy = null;
+
+	/**
+	 * The username to use for the request.
+	 */
+	public $username = null;
+
+	/**
+	 * The password to use for the request.
+	 */
+	public $password = null;
+
+	/**
+	 * Custom CURLOPT settings.
+	 */
+	public $curlopts = null;
+
+	/**
+	 * The state of debug mode.
+	 */
+	public $debug_mode = false;
+
+	/**
+	 * The default class to use for HTTP Requests (defaults to <RequestCore>).
+	 */
+	public $request_class = 'RequestCore';
+
+	/**
+	 * The default class to use for HTTP Responses (defaults to <ResponseCore>).
+	 */
+	public $response_class = 'ResponseCore';
+
+	/**
+	 * Default useragent string to use.
+	 */
+	public $useragent = 'RequestCore/1.4.3';
+
+	/**
+	 * File to read from while streaming up.
+	 */
+	public $read_file = null;
+
+	/**
+	 * The resource to read from while streaming up.
+	 */
+	public $read_stream = null;
+
+	/**
+	 * The size of the stream to read from.
+	 */
+	public $read_stream_size = null;
+
+	/**
+	 * The length already read from the stream.
+	 */
+	public $read_stream_read = 0;
+
+	/**
+	 * File to write to while streaming down.
+	 */
+	public $write_file = null;
+
+	/**
+	 * The resource to write to while streaming down.
+	 */
+	public $write_stream = null;
+
+	/**
+	 * Stores the intended starting seek position.
+	 */
+	public $seek_position = null;
+
+	/**
+	 * The location of the cacert.pem file to use.
+	 */
+	public $cacert_location = false;
+
+	/**
+	 * The state of SSL certificate verification.
+	 */
+	public $ssl_verification = true;
+
+	/**
+	 * The user-defined callback function to call when a stream is read from.
+	 */
+	public $registered_streaming_read_callback = null;
+
+	/**
+	 * The user-defined callback function to call when a stream is written to.
+	 */
+	public $registered_streaming_write_callback = null;
+
+
+	/*%******************************************************************************************%*/
+	// CONSTANTS
+
+	/**
+	 * GET HTTP Method
+	 */
+	const HTTP_GET = 'GET';
+
+	/**
+	 * POST HTTP Method
+	 */
+	const HTTP_POST = 'POST';
+
+	/**
+	 * PUT HTTP Method
+	 */
+	const HTTP_PUT = 'PUT';
+
+	/**
+	 * DELETE HTTP Method
+	 */
+	const HTTP_DELETE = 'DELETE';
+
+	/**
+	 * HEAD HTTP Method
+	 */
+	const HTTP_HEAD = 'HEAD';
+
+
+	/*%******************************************************************************************%*/
+	// CONSTRUCTOR/DESTRUCTOR
+
+	/**
+	 * Constructs a new instance of this class.
+	 *
+	 * @param string $url (Optional) The URL to request or service endpoint to query.
+	 * @param string $proxy (Optional) The faux-url to use for proxy settings. Takes the following format: `proxy://user:pass@hostname:port`
+	 * @param array $helpers (Optional) An associative array of classnames to use for request, and response functionality. Gets passed in automatically by the calling class.
+	 * @return $this A reference to the current instance.
+	 */
+	public function __construct($url = null, $proxy = null, $helpers = null)
+	{
+		// Set some default values.
+		$this->request_url = $url;
+		$this->method = self::HTTP_GET;
+		$this->request_headers = array();
+		$this->request_body = '';
+
+		// Set a new Request class if one was set.
+		if (isset($helpers['request']) && !empty($helpers['request']))
+		{
+			$this->request_class = $helpers['request'];
+		}
+
+		// Set a new Request class if one was set.
+		if (isset($helpers['response']) && !empty($helpers['response']))
+		{
+			$this->response_class = $helpers['response'];
+		}
+
+		if ($proxy)
+		{
+			$this->set_proxy($proxy);
+		}
+
+		return $this;
+	}
+
+	/**
+	 * Destructs the instance. Closes opened file handles.
+	 *
+	 * @return $this A reference to the current instance.
+	 */
+	public function __destruct()
+	{
+		if (isset($this->read_file) && isset($this->read_stream))
+		{
+			fclose($this->read_stream);
+		}
+
+		if (isset($this->write_file) && isset($this->write_stream))
+		{
+			fclose($this->write_stream);
+		}
+
+		return $this;
+	}
+
+
+	/*%******************************************************************************************%*/
+	// REQUEST METHODS
+
+	/**
+	 * Sets the credentials to use for authentication.
+	 *
+	 * @param string $user (Required) The username to authenticate with.
+	 * @param string $pass (Required) The password to authenticate with.
+	 * @return $this A reference to the current instance.
+	 */
+	public function set_credentials($user, $pass)
+	{
+		$this->username = $user;
+		$this->password = $pass;
+		return $this;
+	}
+
+	/**
+	 * Adds a custom HTTP header to the cURL request.
+	 *
+	 * @param string $key (Required) The custom HTTP header to set.
+	 * @param mixed $value (Required) The value to assign to the custom HTTP header.
+	 * @return $this A reference to the current instance.
+	 */
+	public function add_header($key, $value)
+	{
+		$this->request_headers[$key] = $value;
+		return $this;
+	}
+
+	/**
+	 * Removes an HTTP header from the cURL request.
+	 *
+	 * @param string $key (Required) The custom HTTP header to set.
+	 * @return $this A reference to the current instance.
+	 */
+	public function remove_header($key)
+	{
+		if (isset($this->request_headers[$key]))
+		{
+			unset($this->request_headers[$key]);
+		}
+		return $this;
+	}
+
+	/**
+	 * Set the method type for the request.
+	 *
+	 * @param string $method (Required) One of the following constants: <HTTP_GET>, <HTTP_POST>, <HTTP_PUT>, <HTTP_HEAD>, <HTTP_DELETE>.
+	 * @return $this A reference to the current instance.
+	 */
+	public function set_method($method)
+	{
+		$this->method = strtoupper($method);
+		return $this;
+	}
+
+	/**
+	 * Sets a custom useragent string for the class.
+	 *
+	 * @param string $ua (Required) The useragent string to use.
+	 * @return $this A reference to the current instance.
+	 */
+	public function set_useragent($ua)
+	{
+		$this->useragent = $ua;
+		return $this;
+	}
+
+	/**
+	 * Set the body to send in the request.
+	 *
+	 * @param string $body (Required) The textual content to send along in the body of the request.
+	 * @return $this A reference to the current instance.
+	 */
+	public function set_body($body)
+	{
+		$this->request_body = $body;
+		return $this;
+	}
+
+	/**
+	 * Set the URL to make the request to.
+	 *
+	 * @param string $url (Required) The URL to make the request to.
+	 * @return $this A reference to the current instance.
+	 */
+	public function set_request_url($url)
+	{
+		$this->request_url = $url;
+		return $this;
+	}
+
+	/**
+	 * Set additional CURLOPT settings. These will merge with the default settings, and override if
+	 * there is a duplicate.
+	 *
+	 * @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.
+	 * @return $this A reference to the current instance.
+	 */
+	public function set_curlopts($curlopts)
+	{
+		$this->curlopts = $curlopts;
+		return $this;
+	}
+
+	/**
+	 * Sets the length in bytes to read from the stream while streaming up.
+	 *
+	 * @param integer $size (Required) The length in bytes to read from the stream.
+	 * @return $this A reference to the current instance.
+	 */
+	public function set_read_stream_size($size)
+	{
+		$this->read_stream_size = $size;
+
+		return $this;
+	}
+
+	/**
+	 * Sets the resource to read from while streaming up. Reads the stream from its current position until
+	 * EOF or `$size` bytes have been read. If `$size` is not given it will be determined by <php:fstat()> and
+	 * <php:ftell()>.
+	 *
+	 * @param resource $resource (Required) The readable resource to read from.
+	 * @param integer $size (Optional) The size of the stream to read.
+	 * @return $this A reference to the current instance.
+	 */
+	public function set_read_stream($resource, $size = null)
+	{
+		if (!isset($size) || $size < 0)
+		{
+			$stats = fstat($resource);
+
+			if ($stats && $stats['size'] >= 0)
+			{
+				$position = ftell($resource);
+
+				if ($position !== false && $position >= 0)
+				{
+					$size = $stats['size'] - $position;
+				}
+			}
+		}
+
+		$this->read_stream = $resource;
+
+		return $this->set_read_stream_size($size);
+	}
+
+	/**
+	 * Sets the file to read from while streaming up.
+	 *
+	 * @param string $location (Required) The readable location to read from.
+	 * @return $this A reference to the current instance.
+	 */
+	public function set_read_file($location)
+	{
+		$this->read_file = $location;
+		$read_file_handle = fopen($location, 'r');
+
+		return $this->set_read_stream($read_file_handle);
+	}
+
+	/**
+	 * Sets the resource to write to while streaming down.
+	 *
+	 * @param resource $resource (Required) The writeable resource to write to.
+	 * @return $this A reference to the current instance.
+	 */
+	public function set_write_stream($resource)
+	{
+		$this->write_stream = $resource;
+
+		return $this;
+	}
+
+	/**
+	 * Sets the file to write to while streaming down.
+	 *
+	 * @param string $location (Required) The writeable location to write to.
+	 * @return $this A reference to the current instance.
+	 */
+	public function set_write_file($location)
+	{
+		$this->write_file = $location;
+		$write_file_handle = fopen($location, 'w');
+
+		return $this->set_write_stream($write_file_handle);
+	}
+
+	/**
+	 * Set the proxy to use for making requests.
+	 *
+	 * @param string $proxy (Required) The faux-url to use for proxy settings. Takes the following format: `proxy://user:pass@hostname:port`
+	 * @return $this A reference to the current instance.
+	 */
+	public function set_proxy($proxy)
+	{
+		$proxy = parse_url($proxy);
+		$proxy['user'] = isset($proxy['user']) ? $proxy['user'] : null;
+		$proxy['pass'] = isset($proxy['pass']) ? $proxy['pass'] : null;
+		$proxy['port'] = isset($proxy['port']) ? $proxy['port'] : null;
+		$this->proxy = $proxy;
+		return $this;
+	}
+
+	/**
+	 * Set the intended starting seek position.
+	 *
+	 * @param integer $position (Required) The byte-position of the stream to begin reading from.
+	 * @return $this A reference to the current instance.
+	 */
+	public function set_seek_position($position)
+	{
+		$this->seek_position = isset($position) ? (integer) $position : null;
+
+		return $this;
+	}
+
+	/**
+	 * Register a callback function to execute whenever a data stream is read from using
+	 * <CFRequest::streaming_read_callback()>.
+	 *
+	 * The user-defined callback function should accept three arguments:
+	 *
+	 * <ul>
+	 * 	<li><code>$curl_handle</code> - <code>resource</code> - Required - The cURL handle resource that represents the in-progress transfer.</li>
+	 * 	<li><code>$file_handle</code> - <code>resource</code> - Required - The file handle resource that represents the file on the local file system.</li>
+	 * 	<li><code>$length</code> - <code>integer</code> - Required - The length in kilobytes of the data chunk that was transferred.</li>
+	 * </ul>
+	 *
+	 * @param string|array|function $callback (Required) The callback function is called by <php:call_user_func()>, so you can pass the following values: <ul>
+	 * 	<li>The name of a global function to execute, passed as a string.</li>
+	 * 	<li>A method to execute, passed as <code>array('ClassName', 'MethodName')</code>.</li>
+	 * 	<li>An anonymous function (PHP 5.3+).</li></ul>
+	 * @return $this A reference to the current instance.
+	 */
+	public function register_streaming_read_callback($callback)
+	{
+		$this->registered_streaming_read_callback = $callback;
+
+		return $this;
+	}
+
+	/**
+	 * Register a callback function to execute whenever a data stream is written to using
+	 * <CFRequest::streaming_write_callback()>.
+	 *
+	 * The user-defined callback function should accept two arguments:
+	 *
+	 * <ul>
+	 * 	<li><code>$curl_handle</code> - <code>resource</code> - Required - The cURL handle resource that represents the in-progress transfer.</li>
+	 * 	<li><code>$length</code> - <code>integer</code> - Required - The length in kilobytes of the data chunk that was transferred.</li>
+	 * </ul>
+	 *
+	 * @param string|array|function $callback (Required) The callback function is called by <php:call_user_func()>, so you can pass the following values: <ul>
+	 * 	<li>The name of a global function to execute, passed as a string.</li>
+	 * 	<li>A method to execute, passed as <code>array('ClassName', 'MethodName')</code>.</li>
+	 * 	<li>An anonymous function (PHP 5.3+).</li></ul>
+	 * @return $this A reference to the current instance.
+	 */
+	public function register_streaming_write_callback($callback)
+	{
+		$this->registered_streaming_write_callback = $callback;
+
+		return $this;
+	}
+
+
+	/*%******************************************************************************************%*/
+	// PREPARE, SEND, AND PROCESS REQUEST
+
+	/**
+	 * A callback function that is invoked by cURL for streaming up.
+	 *
+	 * @param resource $curl_handle (Required) The cURL handle for the request.
+	 * @param resource $file_handle (Required) The open file handle resource.
+	 * @param integer $length (Required) The maximum number of bytes to read.
+	 * @return binary Binary data from a stream.
+	 */
+	public function streaming_read_callback($curl_handle, $file_handle, $length)
+	{
+		// Once we've sent as much as we're supposed to send...
+		if ($this->read_stream_read >= $this->read_stream_size)
+		{
+			// Send EOF
+			return '';
+		}
+
+		// If we're at the beginning of an upload and need to seek...
+		if ($this->read_stream_read == 0 && isset($this->seek_position) && $this->seek_position !== ftell($this->read_stream))
+		{
+			if (fseek($this->read_stream, $this->seek_position) !== 0)
+			{
+				throw new RequestCore_Exception('The stream does not support seeking and is either not at the requested position or the position is unknown.');
+			}
+		}
+
+		$read = fread($this->read_stream, min($this->read_stream_size - $this->read_stream_read, $length)); // Remaining upload data or cURL's requested chunk size
+		$this->read_stream_read += strlen($read);
+
+		$out = $read === false ? '' : $read;
+
+		// Execute callback function
+		if ($this->registered_streaming_read_callback)
+		{
+			call_user_func($this->registered_streaming_read_callback, $curl_handle, $file_handle, $out);
+		}
+
+		return $out;
+	}
+
+	/**
+	 * A callback function that is invoked by cURL for streaming down.
+	 *
+	 * @param resource $curl_handle (Required) The cURL handle for the request.
+	 * @param binary $data (Required) The data to write.
+	 * @return integer The number of bytes written.
+	 */
+	public function streaming_write_callback($curl_handle, $data)
+	{
+		$length = strlen($data);
+		$written_total = 0;
+		$written_last = 0;
+
+		while ($written_total < $length)
+		{
+			$written_last = fwrite($this->write_stream, substr($data, $written_total));
+
+			if ($written_last === false)
+			{
+				return $written_total;
+			}
+
+			$written_total += $written_last;
+		}
+
+		// Execute callback function
+		if ($this->registered_streaming_write_callback)
+		{
+			call_user_func($this->registered_streaming_write_callback, $curl_handle, $written_total);
+		}
+
+		return $written_total;
+	}
+
+	/**
+	 * Prepares and adds the details of the cURL request. This can be passed along to a <php:curl_multi_exec()>
+	 * function.
+	 *
+	 * @return resource The handle for the cURL object.
+	 */
+	public function prep_request()
+	{
+		$curl_handle = curl_init();
+
+		// Set default options.
+		curl_setopt($curl_handle, CURLOPT_URL, $this->request_url);
+		curl_setopt($curl_handle, CURLOPT_FILETIME, true);
+		curl_setopt($curl_handle, CURLOPT_FRESH_CONNECT, false);
+		curl_setopt($curl_handle, CURLOPT_CLOSEPOLICY, CURLCLOSEPOLICY_LEAST_RECENTLY_USED);
+		curl_setopt($curl_handle, CURLOPT_MAXREDIRS, 5);
+		curl_setopt($curl_handle, CURLOPT_HEADER, true);
+		curl_setopt($curl_handle, CURLOPT_RETURNTRANSFER, true);
+		curl_setopt($curl_handle, CURLOPT_TIMEOUT, 5184000);
+		curl_setopt($curl_handle, CURLOPT_CONNECTTIMEOUT, 120);
+		curl_setopt($curl_handle, CURLOPT_NOSIGNAL, true);
+		curl_setopt($curl_handle, CURLOPT_REFERER, $this->request_url);
+		curl_setopt($curl_handle, CURLOPT_USERAGENT, $this->useragent);
+		curl_setopt($curl_handle, CURLOPT_READFUNCTION, array($this, 'streaming_read_callback'));
+
+		// Verification of the SSL cert
+		if ($this->ssl_verification)
+		{
+			curl_setopt($curl_handle, CURLOPT_SSL_VERIFYPEER, true);
+			curl_setopt($curl_handle, CURLOPT_SSL_VERIFYHOST, true);
+		}
+		else
+		{
+			curl_setopt($curl_handle, CURLOPT_SSL_VERIFYPEER, false);
+			curl_setopt($curl_handle, CURLOPT_SSL_VERIFYHOST, false);
+		}
+
+		// chmod the file as 0755
+		if ($this->cacert_location === true)
+		{
+			curl_setopt($curl_handle, CURLOPT_CAINFO, dirname(__FILE__) . '/cacert.pem');
+		}
+		elseif (is_string($this->cacert_location))
+		{
+			curl_setopt($curl_handle, CURLOPT_CAINFO, $this->cacert_location);
+		}
+
+		// Debug mode
+		if ($this->debug_mode)
+		{
+			curl_setopt($curl_handle, CURLOPT_VERBOSE, true);
+		}
+
+		// Handle open_basedir & safe mode
+		if (!ini_get('safe_mode') && !ini_get('open_basedir'))
+		{
+			curl_setopt($curl_handle, CURLOPT_FOLLOWLOCATION, true);
+		}
+
+		// Enable a proxy connection if requested.
+		if ($this->proxy)
+		{
+			curl_setopt($curl_handle, CURLOPT_HTTPPROXYTUNNEL, true);
+
+			$host = $this->proxy['host'];
+			$host .= ($this->proxy['port']) ? ':' . $this->proxy['port'] : '';
+			curl_setopt($curl_handle, CURLOPT_PROXY, $host);
+
+			if (isset($this->proxy['user']) && isset($this->proxy['pass']))
+			{
+				curl_setopt($curl_handle, CURLOPT_PROXYUSERPWD, $this->proxy['user'] . ':' . $this->proxy['pass']);
+			}
+		}
+
+		// Set credentials for HTTP Basic/Digest Authentication.
+		if ($this->username && $this->password)
+		{
+			curl_setopt($curl_handle, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
+			curl_setopt($curl_handle, CURLOPT_USERPWD, $this->username . ':' . $this->password);
+		}
+
+		// Handle the encoding if we can.
+		if (extension_loaded('zlib'))
+		{
+			curl_setopt($curl_handle, CURLOPT_ENCODING, '');
+		}
+
+		// Process custom headers
+		if (isset($this->request_headers) && count($this->request_headers))
+		{
+			$temp_headers = array();
+
+			foreach ($this->request_headers as $k => $v)
+			{
+				$temp_headers[] = $k . ': ' . $v;
+			}
+
+			curl_setopt($curl_handle, CURLOPT_HTTPHEADER, $temp_headers);
+		}
+
+		switch ($this->method)
+		{
+			case self::HTTP_PUT:
+				//unset($this->read_stream);
+				curl_setopt($curl_handle, CURLOPT_CUSTOMREQUEST, 'PUT');
+				if (isset($this->read_stream))
+				{
+					if (!isset($this->read_stream_size) || $this->read_stream_size < 0)
+					{
+						throw new RequestCore_Exception('The stream size for the streaming upload cannot be determined.');
+					}
+
+					curl_setopt($curl_handle, CURLOPT_INFILESIZE, $this->read_stream_size);
+					curl_setopt($curl_handle, CURLOPT_UPLOAD, true);
+				}
+				else
+				{
+					curl_setopt($curl_handle, CURLOPT_POSTFIELDS, $this->request_body);
+				}
+				break;
+
+			case self::HTTP_POST:
+				curl_setopt($curl_handle, CURLOPT_POST, true);
+				curl_setopt($curl_handle, CURLOPT_POSTFIELDS, $this->request_body);
+				break;
+
+			case self::HTTP_HEAD:
+				curl_setopt($curl_handle, CURLOPT_CUSTOMREQUEST, self::HTTP_HEAD);
+				curl_setopt($curl_handle, CURLOPT_NOBODY, 1);
+				break;
+
+			default: // Assumed GET
+				curl_setopt($curl_handle, CURLOPT_CUSTOMREQUEST, $this->method);
+				if (isset($this->write_stream))
+				{
+					curl_setopt($curl_handle, CURLOPT_WRITEFUNCTION, array($this, 'streaming_write_callback'));
+					curl_setopt($curl_handle, CURLOPT_HEADER, false);
+				}
+				else
+				{
+					curl_setopt($curl_handle, CURLOPT_POSTFIELDS, $this->request_body);
+				}
+				break;
+		}
+
+		// Merge in the CURLOPTs
+		if (isset($this->curlopts) && sizeof($this->curlopts) > 0)
+		{
+			foreach ($this->curlopts as $k => $v)
+			{
+				curl_setopt($curl_handle, $k, $v);
+			}
+		}
+
+		return $curl_handle;
+	}
+
+	/**
+	 * Take the post-processed cURL data and break it down into useful header/body/info chunks. Uses the
+	 * data stored in the `curl_handle` and `response` properties unless replacement data is passed in via
+	 * parameters.
+	 *
+	 * @param resource $curl_handle (Optional) The reference to the already executed cURL request.
+	 * @param string $response (Optional) The actual response content itself that needs to be parsed.
+	 * @return ResponseCore A <ResponseCore> object containing a parsed HTTP response.
+	 */
+	public function process_response($curl_handle = null, $response = null)
+	{
+		// Accept a custom one if it's passed.
+		if ($curl_handle && $response)
+		{
+			$this->curl_handle = $curl_handle;
+			$this->response = $response;
+		}
+
+		// As long as this came back as a valid resource...
+		if (is_resource($this->curl_handle))
+		{
+			// Determine what's what.
+			$header_size = curl_getinfo($this->curl_handle, CURLINFO_HEADER_SIZE);
+			$this->response_headers = substr($this->response, 0, $header_size);
+			$this->response_body = substr($this->response, $header_size);
+			$this->response_code = curl_getinfo($this->curl_handle, CURLINFO_HTTP_CODE);
+			$this->response_info = curl_getinfo($this->curl_handle);
+
+			// Parse out the headers
+			$this->response_headers = explode("\r\n\r\n", trim($this->response_headers));
+			$this->response_headers = array_pop($this->response_headers);
+			$this->response_headers = explode("\r\n", $this->response_headers);
+			array_shift($this->response_headers);
+
+			// Loop through and split up the headers.
+			$header_assoc = array();
+			foreach ($this->response_headers as $header)
+			{
+				$kv = explode(': ', $header);
+				$header_assoc[strtolower($kv[0])] = isset($kv[1])?$kv[1]:'';
+			}
+
+			// Reset the headers to the appropriate property.
+			$this->response_headers = $header_assoc;
+			$this->response_headers['_info'] = $this->response_info;
+			$this->response_headers['_info']['method'] = $this->method;
+
+			if ($curl_handle && $response)
+			{
+				return new $this->response_class($this->response_headers, $this->response_body, $this->response_code, $this->curl_handle);
+			}
+		}
+
+		// Return false
+		return false;
+	}
+
+	/**
+	 * Sends the request, calling necessary utility functions to update built-in properties.
+	 *
+	 * @param boolean $parse (Optional) Whether to parse the response with ResponseCore or not.
+	 * @return string The resulting unparsed data from the request.
+	 */
+	public function send_request($parse = false)
+	{
+		set_time_limit(0);
+
+		$curl_handle = $this->prep_request();
+		$this->response = curl_exec($curl_handle);
+
+		if ($this->response === false)
+		{
+			throw new RequestCore_Exception('cURL resource: ' . (string) $curl_handle . '; cURL error: ' . curl_error($curl_handle) . ' (' . curl_errno($curl_handle) . ')');
+		}
+
+		$parsed_response = $this->process_response($curl_handle, $this->response);
+
+		curl_close($curl_handle);
+
+		if ($parse)
+		{
+			return $parsed_response;
+		}
+
+		return $this->response;
+	}
+
+	/**
+	 * Sends the request using <php:curl_multi_exec()>, enabling parallel requests. Uses the "rolling" method.
+	 *
+	 * @param array $handles (Required) An indexed array of cURL handles to process simultaneously.
+	 * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
+	 * 	<li><code>callback</code> - <code>string|array</code> - Optional - The string name of a function to pass the response data to. If this is a method, pass an array where the <code>[0]</code> index is the class and the <code>[1]</code> index is the method name.</li>
+	 * 	<li><code>limit</code> - <code>integer</code> - 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.</li></ul>
+	 * @return array Post-processed cURL responses.
+	 */
+	public function send_multi_request($handles, $opt = null)
+	{
+		set_time_limit(0);
+
+		// Skip everything if there are no handles to process.
+		if (count($handles) === 0) return array();
+
+		if (!$opt) $opt = array();
+
+		// Initialize any missing options
+		$limit = isset($opt['limit']) ? $opt['limit'] : -1;
+
+		// Initialize
+		$handle_list = $handles;
+		$http = new $this->request_class();
+		$multi_handle = curl_multi_init();
+		$handles_post = array();
+		$added = count($handles);
+		$last_handle = null;
+		$count = 0;
+		$i = 0;
+
+		// Loop through the cURL handles and add as many as it set by the limit parameter.
+		while ($i < $added)
+		{
+			if ($limit > 0 && $i >= $limit) break;
+			curl_multi_add_handle($multi_handle, array_shift($handles));
+			$i++;
+		}
+
+		do
+		{
+			$active = false;
+
+			// Start executing and wait for a response.
+			while (($status = curl_multi_exec($multi_handle, $active)) === CURLM_CALL_MULTI_PERFORM)
+			{
+				// Start looking for possible responses immediately when we have to add more handles
+				if (count($handles) > 0) break;
+			}
+
+			// Figure out which requests finished.
+			$to_process = array();
+
+			while ($done = curl_multi_info_read($multi_handle))
+			{
+				// 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 )
+				if ($done['result'] > 0)
+				{
+					throw new RequestCore_Exception('cURL resource: ' . (string) $done['handle'] . '; cURL error: ' . curl_error($done['handle']) . ' (' . $done['result'] . ')');
+				}
+
+				// 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
+				elseif (!isset($to_process[(int) $done['handle']]))
+				{
+					$to_process[(int) $done['handle']] = $done;
+				}
+			}
+
+			// Actually deal with the request
+			foreach ($to_process as $pkey => $done)
+			{
+				$response = $http->process_response($done['handle'], curl_multi_getcontent($done['handle']));
+				$key = array_search($done['handle'], $handle_list, true);
+				$handles_post[$key] = $response;
+
+				if (count($handles) > 0)
+				{
+					curl_multi_add_handle($multi_handle, array_shift($handles));
+				}
+
+				curl_multi_remove_handle($multi_handle, $done['handle']);
+				curl_close($done['handle']);
+			}
+		}
+		while ($active || count($handles_post) < $added);
+
+		curl_multi_close($multi_handle);
+
+		ksort($handles_post, SORT_NUMERIC);
+		return $handles_post;
+	}
+
+
+	/*%******************************************************************************************%*/
+	// RESPONSE METHODS
+
+	/**
+	 * Get the HTTP response headers from the request.
+	 *
+	 * @param string $header (Optional) A specific header value to return. Defaults to all headers.
+	 * @return string|array All or selected header values.
+	 */
+	public function get_response_header($header = null)
+	{
+		if ($header)
+		{
+			return $this->response_headers[strtolower($header)];
+		}
+		return $this->response_headers;
+	}
+
+	/**
+	 * Get the HTTP response body from the request.
+	 *
+	 * @return string The response body.
+	 */
+	public function get_response_body()
+	{
+		return $this->response_body;
+	}
+
+	/**
+	 * Get the HTTP response code from the request.
+	 *
+	 * @return string The HTTP response code.
+	 */
+	public function get_response_code()
+	{
+		return $this->response_code;
+	}
+}
+
+
+/**
+ * Container for all response-related methods.
+ */
+class ResponseCore
+{
+	/**
+	 * Stores the HTTP header information.
+	 */
+	public $header;
+
+	/**
+	 * Stores the SimpleXML response.
+	 */
+	public $body;
+
+	/**
+	 * Stores the HTTP response code.
+	 */
+	public $status;
+
+	/**
+	 * Constructs a new instance of this class.
+	 *
+	 * @param array $header (Required) Associative array of HTTP headers (typically returned by <RequestCore::get_response_header()>).
+	 * @param string $body (Required) XML-formatted response from AWS.
+	 * @param integer $status (Optional) HTTP response status code from the request.
+	 * @return object Contains an <php:array> `header` property (HTTP headers as an associative array), a <php:SimpleXMLElement> or <php:string> `body` property, and an <php:integer> `status` code.
+	 */
+	public function __construct($header, $body, $status = null)
+	{
+		$this->header = $header;
+		$this->body = $body;
+		$this->status = $status;
+
+		return $this;
+	}
+
+	/**
+	 * Did we receive the status code we expected?
+	 *
+	 * @param integer|array $codes (Optional) The status code(s) to expect. Pass an <php:integer> for a single acceptable value, or an <php:array> of integers for multiple acceptable values.
+	 * @return boolean Whether we received the expected status code or not.
+	 */
+	public function isOK($codes = array(200, 201, 204, 206))
+	{
+		if (is_array($codes))
+		{
+			return in_array($this->status, $codes);
+		}
+
+		return $this->status === $codes;
+	}
+}
+
+/**
+ * Default RequestCore Exception.
+ */
+class RequestCore_Exception extends Exception {}

+ 2309 - 0
include/storages/oss/sdk.class.php

@@ -0,0 +1,2309 @@
+<?php
+/**
+ * OSS(Open Storage Services) PHP SDK v1.1.5
+ */
+//设置默认时区
+date_default_timezone_set('Asia/Shanghai');
+
+//检测API路径
+if(!defined('OSS_API_PATH'))
+define('OSS_API_PATH', dirname(__FILE__));
+
+
+//加载conf.inc.php文件
+require_once OSS_API_PATH.DIRECTORY_SEPARATOR.'conf.inc.php';
+
+//加载RequestCore
+require_once OSS_API_PATH.DIRECTORY_SEPARATOR.'lib'.DIRECTORY_SEPARATOR.'requestcore'.DIRECTORY_SEPARATOR.'requestcore.class.php';
+
+//加载MimeTypes
+require_once OSS_API_PATH.DIRECTORY_SEPARATOR.'util'.DIRECTORY_SEPARATOR.'mimetypes.class.php';
+
+//检测语言包
+if(file_exists(OSS_API_PATH.DIRECTORY_SEPARATOR.'lang'.DIRECTORY_SEPARATOR.ALI_LANG.'.inc.php')){
+	require_once OSS_API_PATH.DIRECTORY_SEPARATOR.'lang'.DIRECTORY_SEPARATOR.ALI_LANG.'.inc.php';
+}else{
+	throw new OSS_Exception(OSS_LANG_FILE_NOT_EXIST);
+}
+
+//定义软件名称,版本号等信息
+define('OSS_NAME','oss-sdk-php');
+define('OSS_VERSION','1.1.5');
+define('OSS_BUILD','201210121010245');
+define('OSS_AUTHOR', 'xiaobing.meng@alibaba-inc.com');
+
+// EXCEPTIONS
+
+/**
+ * OSS异常类,继承自基类
+ */
+class OSS_Exception extends Exception {}
+
+
+//检测get_loaded_extensions函数是否被禁用。由于有些版本把该函数禁用了,所以先检测该函数是否存在。
+if(function_exists('get_loaded_extensions')){
+	//检测curl扩展
+	$extensions = get_loaded_extensions();
+	if($extensions){
+		if(!in_array('curl', $extensions)){
+			throw new OSS_Exception(OSS_CURL_EXTENSION_MUST_BE_LOAD);
+		}
+	}else{
+		throw new OSS_Exception(OSS_NO_ANY_EXTENSIONS_LOADED);
+	}
+}else{
+	throw new OSS_Exception('Function get_loaded_extensions has been disabled,Pls check php config.');
+}
+
+
+//CLASS
+/**
+ * OSS基础类
+ * @author xiaobing.meng@alibaba-inc.com
+ * @since 2012-05-31
+ */
+class ALIOSS{
+	/*%******************************************************************************************%*/
+	// CONSTANTS
+
+	/**
+	 * OSS服务地址
+	 */
+	const DEFAULT_OSS_HOST = 'oss.aliyuncs.com';
+
+	/**
+	 * 软件名称
+	 */
+	const NAME = OSS_NAME;
+
+	/**
+	 * OSS软件Build ID
+	 */
+	const BUILD = OSS_BUILD;
+
+	/**
+	 * 版本号
+	 */
+	const VERSION = OSS_VERSION;
+
+	/**
+	 * 作者
+	 */
+	const AUTHOR = OSS_AUTHOR;
+
+	/*%******************************************************************************************%*/
+	//OSS 内部常量
+
+	const OSS_BUCKET = 'bucket';
+	const OSS_OBJECT = 'object';
+	const OSS_HEADERS = 'headers';
+	const OSS_METHOD = 'method';
+	const OSS_QUERY = 'query';
+	const OSS_BASENAME = 'basename';
+	const OSS_MAX_KEYS = 'max-keys';
+	const OSS_UPLOAD_ID = 'uploadId';
+	const OSS_MAX_KEYS_VALUE = 100;
+	const OSS_MAX_OBJECT_GROUP_VALUE = 1000;
+	const OSS_FILE_SLICE_SIZE = 8192;
+	const OSS_PREFIX = 'prefix';
+	const OSS_DELIMITER = 'delimiter';
+	const OSS_MARKER = 'marker';
+	const OSS_CONTENT_MD5 = 'Content-Md5';
+	const OSS_CONTENT_TYPE = 'Content-Type';
+	const OSS_CONTENT_LENGTH = 'Content-Length';
+	const OSS_IF_MODIFIED_SINCE = 'If-Modified-Since';
+	const OSS_IF_UNMODIFIED_SINCE = 'If-Unmodified-Since';
+	const OSS_IF_MATCH = 'If-Match';
+	const OSS_IF_NONE_MATCH = 'If-None-Match';
+	const OSS_CACHE_CONTROL = 'Cache-Control';
+	const OSS_EXPIRES = 'Expires';
+	const OSS_PREAUTH = 'preauth';
+	const OSS_CONTENT_COING = 'Content-Coding';
+	const OSS_CONTENT_DISPOSTION = 'Content-Disposition';
+	const OSS_RANGE = 'Range';
+	const OS_CONTENT_RANGE = 'Content-Range';
+	const OSS_CONTENT = 'content';
+	const OSS_BODY = 'body';
+	const OSS_LENGTH = 'length';
+	const OSS_HOST = 'Host';
+	const OSS_DATE = 'Date';
+	const OSS_AUTHORIZATION = 'Authorization';
+	const OSS_FILE_DOWNLOAD = 'fileDownload';
+	const OSS_FILE_UPLOAD = 'fileUpload';
+	const OSS_PART_SIZE = 'partSize';
+	const OSS_SEEK_TO = 'seekTo';
+	const OSS_SIZE = 'size';
+	const OSS_QUERY_STRING = 'query_string';
+	const OSS_SUB_RESOURCE = 'sub_resource';
+	const OSS_DEFAULT_PREFIX = 'x-oss-';
+
+	/*%******************************************************************************************%*/
+	//私有URL变量
+
+	const OSS_URL_ACCESS_KEY_ID = 'OSSAccessKeyId';
+	const OSS_URL_EXPIRES = 'Expires';
+	const OSS_URL_SIGNATURE = 'Signature';
+
+	/*%******************************************************************************************%*/
+	//HTTP方法
+
+	const OSS_HTTP_GET = 'GET';
+	const OSS_HTTP_PUT = 'PUT';
+	const OSS_HTTP_HEAD = 'HEAD';
+	const OSS_HTTP_POST = 'POST';
+	const OSS_HTTP_DELETE = 'DELETE';
+
+
+	/*%******************************************************************************************%*/
+	//其他常量
+
+	//x-oss
+	const OSS_ACL = 'x-oss-acl';
+
+	//OBJECT GROUP
+	const OSS_OBJECT_GROUP = 'x-oss-file-group';
+	
+	//Multi Part
+	const OSS_MULTI_PART = 'uploads';
+	
+	//Multi Delete
+	const OSS_MULTI_DELETE = 'delete';
+
+	//OBJECT COPY SOURCE
+	const OSS_OBJECT_COPY_SOURCE = 'x-oss-copy-source';
+
+	//private,only owner
+	const OSS_ACL_TYPE_PRIVATE = 'private';
+
+	//public reand
+	const OSS_ACL_TYPE_PUBLIC_READ = 'public-read';
+
+	//public read write
+	const OSS_ACL_TYPE_PUBLIC_READ_WRITE = 'public-read-write';
+
+	//OSS ACL数组
+	static $OSS_ACL_TYPES = array(
+	self::OSS_ACL_TYPE_PRIVATE,
+	self::OSS_ACL_TYPE_PUBLIC_READ,
+	self::OSS_ACL_TYPE_PUBLIC_READ_WRITE
+	);
+
+
+	/*%******************************************************************************************%*/
+	// PROPERTIES
+
+	/**
+	 * 是否使用SSL
+	 */
+	protected $use_ssl = false;
+
+	/**
+	 * 是否开启debug模式
+	 */
+	private $debug_mode = true;
+	
+	/**
+	 * 最大重试次数
+	 */
+	private $max_retries = 3;
+	
+	/**
+	 * 已经重试次数
+	 */
+	private   $redirects = 0;	
+	
+	/**
+	 * 虚拟地址
+	 */
+	private $vhost;
+	
+	/**
+	 * 路径表现方式
+	 */
+	private $enable_domain_style = true;
+	
+	/**
+	 * 请求URL
+	 */
+	private  $request_url;
+	
+	/**
+	 * OSS API ACCESS ID
+	 */
+	private $access_id;
+
+	/**
+	 * OSS API ACCESS KEY
+	 */
+	private $access_key;
+
+	/**
+	 * hostname
+	 */
+	private $hostname;
+	
+	/**
+	 * port number
+	 */
+	private $port;
+
+	/*%******************************************************************************************************%*/
+	//Constructor
+
+	/**
+	 * 默认构造函数
+	 * @param string $_access_id (Optional)
+	 * @param string $access_key (Optional)
+	 * @param string $hostname (Optional)
+	 * @throws OSS_Exception
+	 * @author	xiaobing.meng@alibaba-inc.com
+	 * @since	2011-11-08
+	 */
+	public function __construct($access_id = NULL,$access_key = NULL, $hostname = NULL  ){
+		//验证access_id,access_key
+		if(!$access_id && !defined('OSS_ACCESS_ID')){
+				throw new OSS_Exception(NOT_SET_OSS_ACCESS_ID);
+		}
+
+		if(!$access_key && !defined('OSS_ACCESS_KEY')){
+			throw new OSS_Exception(NOT_SET_OSS_ACCESS_KEY);
+		}
+
+		if($access_id && $access_key){
+			$this->access_id = $access_id;
+			$this->access_key = $access_key;
+		}elseif (defined('OSS_ACCESS_ID') && defined('OSS_ACCESS_KEY')){
+			$this->access_id = OSS_ACCESS_ID;
+			$this->access_key = OSS_ACCESS_KEY;
+		}else{
+			throw new OSS_Exception(NOT_SET_OSS_ACCESS_ID_AND_ACCESS_KEY);
+		}
+
+		//校验access_id&access_key 
+		if(empty($this->access_id) || empty($this->access_key)){
+			throw new OSS_Exception(OSS_ACCESS_ID_OR_ACCESS_KEY_EMPTY);
+		}
+
+		//校验hostname
+		if(NULL === $hostname){
+			$this->hostname = self::DEFAULT_OSS_HOST;
+		}else{
+			$this->hostname = $hostname;
+		}
+	}
+
+
+	/*%******************************************************************************************************%*/
+	//属性
+	
+	/**
+	 * 设置debug模式
+	 * @param boolean $debug_mode (Optional)
+	 * @author xiaobing.meng@alibaba-inc.com
+	 * @since 2012-05-29
+	 * @return void
+	 */
+	public function set_debug_mode($debug_mode = true){
+		$this->debug_mode = $debug_mode;
+	}
+	
+	/**
+	 * 设置最大尝试次数
+	 * @param int $max_retries
+	 * @author xiaobing.meng@alibaba-inc.com
+	 * @since 2012-05-29
+	 * @return void
+	 */
+	public function set_max_retries($max_retries = 3){
+		$this->max_retries = $max_retries;
+	}
+	
+	/**
+	 * 获取最大尝试次数
+	 * @author xiaobing.meng@alibaba-inc.com
+	 * @since 2012-05-29
+	 * @return int
+	 */
+	public function get_max_retries(){
+		return $this->max_retries;
+	}
+
+	/**
+	 * 设置host地址
+	 * @author xiaobing.meng@alibaba-inc.com
+	 * @param string $hostname host name
+	 * @param int	$port int
+	 * @since 2012-06-11
+	 * @return void
+	 */
+	public function set_host_name($hostname,$port = null){
+		$this->hostname = $hostname;
+		
+		if($port){
+			$this->port = $port;
+			$this->hostname .= ':'.$port;
+		}
+	}
+	
+	/**
+	 * 设置vhost地址
+	 * @author xiaobing.meng@alibaba-inc.com
+	 * @param string $vhost vhost
+	 * @since 2012-06-11
+	 * @return void
+	 */
+	public function set_vhost($vhost){
+		$this->vhost = $vhost;
+	}
+	
+	/**
+	 * 设置路径形式,如果为true,则启用三级域名,如bucket.oss.aliyuncs.com
+	 * @author xiaobing.meng@alibaba-inc.com
+	 * @param boolean $enable_domain_style 
+	 * @since 2012-06-11
+	 * @return void
+	 */
+	public function set_enable_domain_style($enable_domain_style = true){
+		$this->enable_domain_style = $enable_domain_style;
+	}
+
+	
+	/*%******************************************************************************************************%*/
+	//请求
+
+	/**
+	 * Authorization
+	 * @param array $options (Required)
+	 * @throws OSS_Exception
+	 * @author xiaobing.meng@alibaba-inc.com
+	 * @since 2012-05-31
+	 */
+	public function auth($options){
+		//开始记录LOG
+		$msg = "---LOG START---------------------------------------------------------------------------\n";
+		
+		//验证Bucket,list_bucket时不需要验证
+		if(!( ('/' == $options[self::OSS_OBJECT]) && ('' == $options[self::OSS_BUCKET]) && ('GET' == $options[self::OSS_METHOD])) && !$this->validate_bucket($options[self::OSS_BUCKET])){
+			throw new OSS_Exception('"'.$options[self::OSS_BUCKET].'"'.OSS_BUCKET_NAME_INVALID);
+		}
+	
+		//验证Object
+		if(isset($options[self::OSS_OBJECT]) && !$this->validate_object($options[self::OSS_OBJECT])){
+			throw  new OSS_Exception($options[self::OSS_OBJECT].OSS_OBJECT_NAME_INVALID);
+		}
+		
+		//Object编码为UTF-8
+		if($this->is_gb2312($options[self::OSS_OBJECT])){
+			$options[self::OSS_OBJECT] = iconv('GB2312', "UTF-8",$options[self::OSS_OBJECT]);
+		}elseif($this->check_char($options[self::OSS_OBJECT],true)){
+			$options[self::OSS_OBJECT] = iconv('GBK', "UTF-8",$options[self::OSS_OBJECT]);
+		}	
+
+		
+		//验证ACL
+		if(isset($options[self::OSS_HEADERS][self::OSS_ACL]) && !empty($options[self::OSS_HEADERS][self::OSS_ACL])){
+			if(!in_array(strtolower($options[self::OSS_HEADERS][self::OSS_ACL]), self::$OSS_ACL_TYPES)){
+				throw new OSS_Exception($options[self::OSS_HEADERS][self::OSS_ACL].':'.OSS_ACL_INVALID);
+			}
+		}
+
+				
+		//定义scheme
+		$scheme = $this->use_ssl ? 'https://' : 'http://';
+		
+		//匹配bucket
+		if(strpos($options[self::OSS_BUCKET],"-")!== false){//bucket 带"-"的时候
+			$this->set_enable_domain_style(false);
+		}else{
+			$this->set_enable_domain_style(true);
+		}
+		
+		if($this->enable_domain_style){
+			$hostname = $this->vhost ? $this->vhost : (($options[self::OSS_BUCKET] =='')?$this->hostname:($options[self::OSS_BUCKET].'.').$this->hostname);
+		}else{
+			$hostname = (isset($options[self::OSS_BUCKET]) && ''!==$options[self::OSS_BUCKET])?$this->hostname.'/'.$options[self::OSS_BUCKET]:$this->hostname;
+		}
+
+		
+		//请求参数
+		$resource = '';
+		$sub_resource = '';
+		$signable_resource = '';
+		$query_string_params = array();
+		$signable_query_string_params = array();
+		$string_to_sign = '';		
+		
+		$headers = array (
+			self::OSS_CONTENT_MD5 => '',
+			self::OSS_CONTENT_TYPE => isset($options[self::OSS_CONTENT_TYPE])?$options[self::OSS_CONTENT_TYPE]:'application/x-www-form-urlencoded',
+			self::OSS_DATE => isset($options[self::OSS_DATE])? $options[self::OSS_DATE]: gmdate('D, d M Y H:i:s \G\M\T'),
+			self::OSS_HOST => $this->enable_domain_style?$hostname:$this->hostname,
+		);
+
+		if(isset ( $options [self::OSS_OBJECT] ) && '/' !== $options [self::OSS_OBJECT]){
+			$signable_resource = '/'.str_replace('%2F', '/', rawurlencode($options[self::OSS_OBJECT]));
+		}
+
+		if(isset($options[self::OSS_QUERY_STRING])){
+			$query_string_params = array_merge($query_string_params,$options[self::OSS_QUERY_STRING]);
+		}
+		$query_string = $this->to_query_string($query_string_params);
+	
+		$signable_list = array(
+			'partNumber',
+			'uploadId',			
+		);
+		
+		foreach ($signable_list as $item){
+			if(isset($options[$item])){
+				$signable_query_string_params[$item] = $options[$item]; 
+			}
+		}
+		$signable_query_string = $this->to_query_string($signable_query_string_params);
+		
+		//合并 HTTP headers
+		if (isset ( $options [self::OSS_HEADERS] )) {
+			$headers = array_merge ( $headers, $options [self::OSS_HEADERS] );
+		}
+		
+		//生成请求URL
+		$conjunction = '?';
+		
+		$non_signable_resource = '';
+
+		if (isset($options[self::OSS_SUB_RESOURCE])){
+			$signable_resource .= $conjunction . $options[self::OSS_SUB_RESOURCE];
+			$conjunction = '&';
+		}	
+
+		if($signable_query_string !== ''){
+			$signable_query_string = $conjunction.$signable_query_string;
+			$conjunction = '&';
+		}
+		
+		if($query_string !== ''){
+			$non_signable_resource .= $conjunction . $query_string;
+			$conjunction = '&';			
+		}
+		
+		$this->request_url = 	 $scheme . $hostname . $signable_resource . $signable_query_string . $non_signable_resource;
+
+		$msg .= "--REQUEST URL:----------------------------------------------\n".$this->request_url."\n";
+		
+		//创建请求
+		$request = new RequestCore($this->request_url);
+		 
+		// Streaming uploads
+		if (isset($options[self::OSS_FILE_UPLOAD])){
+			if (is_resource($options[self::OSS_FILE_UPLOAD])){
+				$length = null; 
+
+				if (isset($options[self::OSS_CONTENT_LENGTH])){
+					$length = $options[self::OSS_CONTENT_LENGTH];
+				}elseif (isset($options[self::OSS_SEEK_TO])){
+					
+					$stats = fstat($options[self::OSS_FILE_UPLOAD]);
+
+					if ($stats && $stats[self::OSS_SIZE] >= 0){
+						$length = $stats[self::OSS_SIZE] - (integer) $options[self::OSS_SEEK_TO];
+					}
+				}
+
+				$request->set_read_stream($options[self::OSS_FILE_UPLOAD], $length);
+
+				if ($headers[self::OSS_CONTENT_TYPE] === 'application/x-www-form-urlencoded'){
+					$headers[self::OSS_CONTENT_TYPE] = 'application/octet-stream';
+				}
+			}else{
+				$request->set_read_file($options[self::OSS_FILE_UPLOAD]);
+
+				$length = $request->read_stream_size; 
+
+				if (isset($options[self::OSS_CONTENT_LENGTH])){
+					$length = $options[self::OSS_CONTENT_LENGTH];
+				}elseif (isset($options[self::OSS_SEEK_TO]) && isset($length)){
+					$length -= (integer) $options[self::OSS_SEEK_TO];
+				}
+
+				$request->set_read_stream_size($length);
+
+				if (isset($headers[self::OSS_CONTENT_TYPE]) && ($headers[self::OSS_CONTENT_TYPE] === 'application/x-www-form-urlencoded')){
+					$extension = explode('.', $options[self::OSS_FILE_UPLOAD]);
+					$extension = array_pop($extension);
+					$mime_type = MimeTypes::get_mimetype($extension);
+					$headers[self::OSS_CONTENT_TYPE] = $mime_type;
+				}
+			}
+
+			$options[self::OSS_CONTENT_MD5] = '';
+		}
+
+		if (isset($options[self::OSS_SEEK_TO])){
+			$request->set_seek_position((integer) $options[self::OSS_SEEK_TO]);
+		}	
+
+		if (isset($options[self::OSS_FILE_DOWNLOAD])){
+			if (is_resource($options[self::OSS_FILE_DOWNLOAD])){
+				$request->set_write_stream($options[self::OSS_FILE_DOWNLOAD]);
+			}else{
+				$request->set_write_file($options[self::OSS_FILE_DOWNLOAD]);
+			}
+		}		
+		
+		
+		if(isset($options[self::OSS_METHOD])){
+			$request->set_method($options[self::OSS_METHOD]);
+			$string_to_sign .= $options[self::OSS_METHOD] . "\n";			
+		}
+		
+		if (isset ( $options [self::OSS_CONTENT] )) {
+			$request->set_body ( $options [self::OSS_CONTENT] );
+			if ($headers[self::OSS_CONTENT_TYPE] === 'application/x-www-form-urlencoded'){
+				$headers[self::OSS_CONTENT_TYPE] = 'application/octet-stream';
+			}			
+			
+			$headers[self::OSS_CONTENT_LENGTH] = strlen($options [self::OSS_CONTENT]);
+			$headers[self::OSS_CONTENT_MD5] = $this->hex_to_base64(md5($options[self::OSS_CONTENT]));
+		}
+
+		uksort($headers, 'strnatcasecmp');
+		
+		foreach ( $headers as $header_key => $header_value ) {
+			$header_value = str_replace ( array ("\r", "\n" ), '', $header_value );
+			if ($header_value !== '') {
+				$request->add_header ( $header_key, $header_value );
+			}
+
+			if (
+				strtolower($header_key) === 'content-md5' ||
+				strtolower($header_key) === 'content-type' ||
+				strtolower($header_key) === 'date' ||
+				(isset($options['self::OSS_PREAUTH']) && (integer) $options['self::OSS_PREAUTH'] > 0)
+			){
+				$string_to_sign .= $header_value . "\n";
+			}elseif (substr(strtolower($header_key), 0, 6) === self::OSS_DEFAULT_PREFIX){
+				$string_to_sign .= strtolower($header_key) . ':' . $header_value . "\n";
+			}			
+		}
+	
+		$string_to_sign .= '/' . $options[self::OSS_BUCKET];
+		$string_to_sign .=  $this->enable_domain_style ? ($options[self::OSS_BUCKET]!=''? ($options[self::OSS_OBJECT]=='/'?'/':'') :'' ) : '';
+		$string_to_sign .= rawurldecode($signable_resource) . urldecode($signable_query_string);
+
+		$msg .= "STRING TO SIGN:----------------------------------------------\n".$string_to_sign."\n";
+		
+		$signature = base64_encode(hash_hmac('sha1', $string_to_sign, $this->access_key, true));
+		$request->add_header('Authorization', 'OSS ' . $this->access_id . ':' . $signature);
+
+		if (isset($options[self::OSS_PREAUTH]) && (integer) $options[self::OSS_PREAUTH] > 0){
+			return $this->request_url . $conjunction . self::OSS_URL_ACCESS_KEY_ID.'=' . $this->access_id . '&'.self::OSS_URL_EXPIRES.'=' . $options[self::OSS_PREAUTH] . '&'.self::OSS_URL_SIGNATURE.'=' . rawurlencode($signature);
+		}elseif (isset($options[self::OSS_PREAUTH])){
+			return $this->request_url;
+		}		
+		
+		if ($this->debug_mode){
+			$request->debug_mode = $this->debug_mode;
+		}
+
+		$msg .= "REQUEST HEADERS:----------------------------------------------\n".serialize($request->request_headers)."\n";
+		
+		$request->send_request();
+
+		$response_header = $request->get_response_header();
+		$response_header['x-oss-request-url'] = $this->request_url;
+		$response_header['x-oss-redirects'] = $this->redirects;
+		$response_header['x-oss-stringtosign'] = $string_to_sign;
+		$response_header['x-oss-requestheaders'] = $request->request_headers;				
+		
+		$msg .= "RESPONSE HEADERS:----------------------------------------------\n".serialize($response_header)."\n";
+		
+		$data =  new ResponseCore ( $response_header , $request->get_response_body (), $request->get_response_code () );
+		
+		if((integer)$request->get_response_code() === 400 /*Bad Request*/ || (integer)$request->get_response_code() === 500 /*Internal Error*/ || (integer)$request->get_response_code() === 503 /*Service Unavailable*/){	
+		   if($this->redirects <= $this->max_retries ){
+		   		//设置休眠
+		   		$delay = (integer) (pow(4, $this->redirects) * 100000);
+		   		usleep($delay);
+		   		$this->redirects++;
+		   		$data = $this->auth($options);
+		   }
+		}
+		
+		$msg .= "RESPONSE DATA:----------------------------------------------\n".serialize($data)."\n";
+		$msg .= date('Y-m-d H:i:s').":---LOG END---------------------------------------------------------------------------\n";
+		//add log
+		$this->log($msg);
+		
+		$this->redirects = 0;	
+		return $data;
+	}
+
+
+	/*%******************************************************************************************************%*/
+	//Service Operation
+
+	/**
+	 * Get Buket list
+	 * @param array $options (Optional)
+	 * @throws OSS_Exception
+	 * @author xiaobing.meng@alibaba-inc.com
+	 * @since 2011-11-14
+	 * @return ResponseCore
+	 */
+	public function list_bucket($options = NULL) {
+		//$options
+		$this->validate_options($options);
+
+		if (! $options) {
+			$options = array ();
+		}
+
+		$options[self::OSS_BUCKET] = '';
+		$options[self::OSS_METHOD] = 'GET';
+		$options[self::OSS_OBJECT] = '/';
+		$response = $this->auth ( $options );
+
+		return $response;
+	}
+
+
+	/*%******************************************************************************************************%*/
+	//Bucket Operation
+
+	/**
+	 * Create Bucket
+	 * @param string $bucket (Required)
+	 * @param string $acl (Optional)
+	 * @param array $options (Optional)
+	 * @author xiaobing.meng@alibaba-inc.com
+	 * @since 2011-11-14
+	 * @return ResponseCore
+	 */
+	public function create_bucket($bucket,$acl = self::OSS_ACL_TYPE_PRIVATE, $options = NULL){
+		//$options
+		$this->validate_options($options);
+
+		if (! $options) {
+			$options = array ();
+		}
+
+		//bucket
+		$this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY);
+
+		$options[self::OSS_BUCKET] = $bucket;
+		$options[self::OSS_METHOD] = 'PUT';
+		$options[self::OSS_OBJECT] = '/';
+		$options[self::OSS_HEADERS] = array(self::OSS_ACL => $acl);
+		$response = $this->auth ( $options );
+
+		return $response;
+	}
+
+	/**
+	 * Delete Bucket
+	 * @param string $bucket (Required)
+	 * @param array $options (Optional)
+	 * @author xiaobing.meng@alibaba-inc.com
+	 * @since 2011-11-14
+	 * @return ResponseCore
+	 */
+	public function delete_bucket($bucket,$options = NULL){
+		//$options
+		$this->validate_options($options);
+
+		if (! $options) {
+			$options = array ();
+		}
+
+		//bucket
+		$this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY);
+
+		$options[self::OSS_BUCKET] = $bucket;
+		$options[self::OSS_METHOD] = 'DELETE';
+		$options[self::OSS_OBJECT] = '/';
+		$response = $this->auth ( $options );
+
+		return $response;
+	}
+
+	/**
+	 * Get Bucket's ACL
+	 * @param string $bucket (Required)
+	 * @param array $options (Optional)
+	 * @throws OSS_Exception
+	 * @author xiaobing.meng@alibaba-inc.com
+	 * @since 2011-11-14
+	 * @return ResponseCore
+	 */
+	public function get_bucket_acl($bucket ,$options = NULL){
+		//options
+		$this->validate_options($options);
+
+		if(!$options){
+			$options = array();
+		}
+
+		//bucket
+		$this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY);
+
+		$options[self::OSS_BUCKET] = $bucket;
+		$options[self::OSS_METHOD] = 'GET';
+		$options[self::OSS_OBJECT] = '/';
+		$options[self::OSS_SUB_RESOURCE] = 'acl';
+		$response = $this->auth ( $options );
+
+		return $response;
+	}
+
+	/**
+	 * Set bucket'ACL
+	 * @param string $bucket (Required)
+	 * @param string $acl  (Required)
+	 * @param array $options (Optional)
+	 * @throws OSS_Exception
+	 * @author xiaobing.meng@alibaba-inc.com
+	 * @since 2011-11-14
+	 * @return ResponseCore
+	 */
+	public function set_bucket_acl($bucket ,$acl , $options = NULL){
+		//options
+		$this->validate_options($options);
+
+		if(!$options){
+			$options = array();
+		}
+
+		//bucket
+		$this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY);
+
+		$options[self::OSS_BUCKET] = $bucket;
+		$options[self::OSS_METHOD] = 'PUT';
+		$options[self::OSS_OBJECT] = '/';
+		$options[self::OSS_HEADERS] = array(self::OSS_ACL => $acl);
+		$response = $this->auth ( $options );
+
+		return $response;
+	}
+
+
+	/*%******************************************************************************************************%*/
+	//Object Operation
+
+	/**
+	 * List Object
+	 * @param string $bucket (Required)
+	 * @param array $options (Optional)
+	 * 其中options中的参数如下
+	 * $options = array(
+	 * 		'max-keys' 	=> max-keys用于限定此次返回object的最大数,如果不设定,默认为100,max-keys取值不能大于100。
+	 * 		'prefix'	=> 限定返回的object key必须以prefix作为前缀。注意使用prefix查询时,返回的key中仍会包含prefix。
+	 * 		'delimiter' => 是一个用于对Object名字进行分组的字符。所有名字包含指定的前缀且第一次出现delimiter字符之间的object作为一组元素
+	 * 		'marker'	=> 用户设定结果从marker之后按字母排序的第一个开始返回。
+	 * )
+	 * 其中 prefix,marker用来实现分页显示效果,参数的长度必须小于256字节。
+	 * @throws OSS_Exception
+	 * @author xiaobing.meng@alibaba-inc.com
+	 * @since 2011-11-14
+	 * @return ResponseCore
+	 */
+	public function list_object($bucket,$options = NULL){
+		//options
+		$this->validate_options($options);
+
+		if(!$options){
+			$options = array();
+		}
+
+		//bucket
+		$this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY);
+
+		$options[self::OSS_BUCKET] = $bucket;
+		$options[self::OSS_METHOD] = 'GET';
+		$options[self::OSS_OBJECT] = '/';
+		$options[self::OSS_HEADERS] = array(
+		self::OSS_DELIMITER => isset($options[self::OSS_DELIMITER])?$options[self::OSS_DELIMITER]:'/',
+		self::OSS_PREFIX => isset($options[self::OSS_PREFIX])?$options[self::OSS_PREFIX]:'',
+		self::OSS_MAX_KEYS => isset($options[self::OSS_MAX_KEYS])?$options[self::OSS_MAX_KEYS]:self::OSS_MAX_KEYS_VALUE,
+		self::OSS_MARKER => isset($options[self::OSS_MARKER])?$options[self::OSS_MARKER]:'',
+		);
+				
+		$response = $this->auth ( $options );
+
+		return $response;
+
+	}
+
+	/**
+	 * 创建目录(目录和文件的区别在于,目录最后增加'/')
+	 * @param string $bucket
+	 * @param string $object
+	 * @param array $options
+	 * @author xiaobing.meng@alibaba-inc.com
+	 * @since 2011-11-14
+	 * @return ResponseCore
+	 */
+	public function create_object_dir($bucket,$object,$options = NULL){
+		//options
+		$this->validate_options($options);
+
+		if(!$options){
+			$options = array();
+		}
+
+		//bucket
+		$this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY);
+
+		//object
+		$this->is_empty($object,OSS_OBJECT_IS_NOT_ALLOWED_EMPTY);
+
+		$options[self::OSS_BUCKET] = $bucket;
+		$options[self::OSS_METHOD] = 'PUT';
+		$options[self::OSS_OBJECT] = $object.'/';   //虚拟目录需要以'/结尾'
+		$options[self::OSS_CONTENT_LENGTH] = array(self::OSS_CONTENT_LENGTH => 0);
+
+		$response = $this->auth ( $options );
+
+		return $response;
+	}
+
+	/**
+	 * 通过在http body中添加内容来上传文件,适合比较小的文件
+	 * 根据api约定,需要在http header中增加content-length字段
+	 * @param string $bucket (Required)
+	 * @param string $object (Required)
+	 * @param string $content (Required)
+	 * @param array $options (Optional)
+	 * @author xiaobing.meng@alibaba-inc.com
+	 * @since 2011-11-14
+	 * @return ResponseCore
+	 */
+	public function upload_file_by_content($bucket,$object,$options = NULL){
+		//options
+		$this->validate_options($options);
+
+		if(!$options){
+			$options = array();
+		}
+
+		//bucket
+		$this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY);
+
+		//object
+		$this->is_empty($object,OSS_OBJECT_IS_NOT_ALLOWED_EMPTY);
+
+		//内容校验
+		$this->validate_content($options);
+
+			
+		$objArr = explode('/', $object);
+		$basename = array_pop($objArr);
+		$extension = explode ( '.', $basename );
+		$extension = array_pop ( $extension );
+		$content_type = MimeTypes::get_mimetype(strtolower($extension));
+
+		$options[self::OSS_BUCKET] = $bucket;
+		$options[self::OSS_METHOD] = 'PUT';
+		$options[self::OSS_OBJECT] = $object;
+		
+		if(!isset($options[self::OSS_LENGTH])){
+			$options[self::OSS_CONTENT_LENGTH] = strlen($options[self::OSS_CONTENT]);
+		}else{
+			$options[self::OSS_CONTENT_LENGTH] = $options[self::OSS_LENGTH];
+		}
+		
+		if(!isset($options[self::OSS_CONTENT_TYPE]) && isset($content_type) && !empty($content_type) ){
+			$options[self::OSS_CONTENT_TYPE] = $content_type;
+		}
+
+		$response = $this->auth ( $options );
+
+		return $response;
+	}
+
+	/**
+	 * 上传文件,适合比较大的文件
+	 * @param string $bucket (Required)
+	 * @param string $object (Required)
+	 * @param string $file (Required)
+	 * @param array $options (Optional)
+	 * @author xiaobing.meng@alibaba-inc.com
+	 * @since 2012-02-28
+	 * @return ResponseCore
+	 */
+	public function upload_file_by_file($bucket,$object,$file,$options = NULL){
+		//options
+		$this->validate_options($options);
+
+		if(!$options){
+			$options = array();
+		}
+
+		//bucket
+		$this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY);
+
+		//object
+		$this->is_empty($object,OSS_OBJECT_IS_NOT_ALLOWED_EMPTY);
+
+		//file
+		$this->is_empty($file, OSS_FILE_PATH_IS_NOT_ALLOWED_EMPTY);
+		
+		if($this->chk_chinese($file)){
+			$file = iconv('utf-8','gbk',$file);
+		}
+		
+		$options[self::OSS_FILE_UPLOAD] = $file;
+		
+		if(!file_exists($options[self::OSS_FILE_UPLOAD])){
+			throw new OSS_Exception($options[self::OSS_FILE_UPLOAD].OSS_FILE_NOT_EXIST);
+		}
+		
+		$filesize = filesize($options[self::OSS_FILE_UPLOAD]);
+		$partsize = 1024 * 1024 ; //默认为 1M
+		
+		
+		$extension = explode ( '.', $file );
+		$extension = array_pop ( $extension );
+		$content_type = MimeTypes::get_mimetype(strtolower($extension));
+				
+		$options[self::OSS_METHOD] = self::OSS_HTTP_PUT;
+		$options[self::OSS_BUCKET] = $bucket;
+		$options[self::OSS_OBJECT] = $object;
+		$options[self::OSS_CONTENT_TYPE] = $content_type;
+		$options[self::OSS_CONTENT_LENGTH] = $filesize;
+				
+		$response = $this->auth($options);
+		return $response;
+	}
+	
+	
+	/**
+	 * 拷贝Object
+	 * @param string $bucket (Required)
+	 * @param string $from_object (Required)
+	 * @param string $to_object (Required)
+	 * @param string $options (Optional)
+	 * @author xiaobing.meng@alibaba-inc.com
+	 * @since 2011-12-21
+	 * @return ResponseCore
+	 */
+	public function copy_object($from_bucket,$from_object,$to_bucket,$to_object,$options = NULL){
+		//options
+		$this->validate_options($options);
+
+		if(!$options){
+			$options = array();
+		}
+
+		//from bucket
+		$this->is_empty($from_bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY);
+
+		//to bucket
+		$this->is_empty($to_bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY);
+
+		//from object
+		$this->is_empty($from_object,OSS_OBJECT_IS_NOT_ALLOWED_EMPTY);
+
+		//to object
+		$this->is_empty($to_object,OSS_OBJECT_IS_NOT_ALLOWED_EMPTY);
+
+		$options[self::OSS_BUCKET] = $to_bucket;
+		$options[self::OSS_METHOD] = 'PUT';
+		$options[self::OSS_OBJECT] = $to_object;
+		$options[self::OSS_HEADERS] = array(self::OSS_OBJECT_COPY_SOURCE => '/'.$from_bucket.'/'.$from_object);
+
+		$response = $this->auth ( $options );
+
+		return $response;
+	}
+
+	/**
+	 * 获得object的meta信息
+	 * @param string $bucket (Required)
+	 * @param string $object (Required)
+	 * @param string $options (Optional)
+	 * @author xiaobing.meng@alibaba-inc.com
+	 * @since 2011-11-14
+	 * @return ResponseCore
+	 */
+	public function get_object_meta($bucket,$object,$options = NULL){
+		//options
+		$this->validate_options($options);
+
+		if(!$options){
+			$options = array();
+		}
+
+		//bucket
+		$this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY);
+
+		//object
+		$this->is_empty($object,OSS_OBJECT_IS_NOT_ALLOWED_EMPTY);
+
+		$options[self::OSS_BUCKET] = $bucket;
+		$options[self::OSS_METHOD] = 'HEAD';
+		$options[self::OSS_OBJECT] = $object;
+
+		$response = $this->auth ( $options );
+
+		return $response;
+	}
+
+	/**
+	 * 删除object
+	 * @param string $bucket(Required)
+	 * @param string $object (Required)
+	 * @param array $options (Optional)
+	 * @author xiaobing.meng@alibaba-inc.com
+	 * @since 2011-11-14
+	 * @return ResponseCore
+	 */
+	public function delete_object($bucket,$object,$options = NULL){
+		//options
+		$this->validate_options($options);
+
+		if(!$options){
+			$options = array();
+		}
+
+		//bucket
+		$this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY);
+
+		//object
+		$this->is_empty($object,OSS_OBJECT_IS_NOT_ALLOWED_EMPTY);
+
+		$options[self::OSS_BUCKET] = $bucket;
+		$options[self::OSS_METHOD] = 'DELETE';
+		$options[self::OSS_OBJECT] = $object;
+
+		$response = $this->auth ( $options );
+
+		return $response;
+	}
+	
+	/**
+	 * 批量删除objects
+	 * @param string $bucket(Required)
+	 * @param array $objects (Required)
+	 * @param array $options (Optional)
+	 * @author xiaobing.meng@alibaba-inc.com
+	 * @since 2012-03-09
+	 * @return ResponseCore
+	 */
+	public function delete_objects($bucket,$objects,$options = null){
+		//options
+		$this->validate_options($options);
+
+		if(!$options){
+			$options = array();
+		}
+
+		//bucket
+		$this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY);
+
+		//objects
+		if(!is_array($objects) || !$objects){
+			throw new OSS_Exception('The ' . __FUNCTION__ . ' method requires the "objects" option to be set as an array.');
+		}
+		
+		$options[self::OSS_METHOD] = self::OSS_HTTP_POST;
+		$options[self::OSS_BUCKET] = $bucket;
+		$options[self::OSS_OBJECT] = '/';
+		$options[self::OSS_SUB_RESOURCE] = 'delete';
+		$options[self::OSS_CONTENT_TYPE] = 'application/xml';
+		
+		$xml = new SimpleXMLElement('<?xml version="1.0" encoding="utf-8"?><Delete></Delete>');
+		
+		// Quiet mode?
+		if (isset($options['quiet'])){
+			$quiet = 'false';
+			if (is_bool($options['quiet'])) { //Boolean
+				$quiet = $options['quiet'] ? 'true' : 'false';
+			}elseif (is_string($options['quiet'])){ // String
+				$quiet = ($options['quiet'] === 'true') ? 'true' : 'false';
+			}
+
+			$xml->addChild('Quiet', $quiet);
+		}
+				
+		// Add the objects
+		foreach ($objects as $object){
+			$xobject = $xml->addChild('Object');
+			$object = $this->s_replace($object);		
+			$xobject->addChild('Key', $object);
+		}		
+
+		$options[self::OSS_CONTENT] = $xml->asXML();		
+			
+		return $this->auth($options);
+	}
+
+	/**
+	 * 获得Object内容
+	 * @param string $bucket(Required)
+	 * @param string $object (Required)
+	 * @param array $options (Optional)
+	 * @author xiaobing.meng@alibaba-inc.com
+	 * @since 2011-11-14
+	 * @return ResponseCore
+	 */
+	public function get_object($bucket,$object,$options = NULL){
+		//options
+		$this->validate_options($options);
+
+		if(!$options){
+			$options = array();
+		}
+
+		//bucket
+		$this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY);
+
+		//object
+		$this->is_empty($object,OSS_OBJECT_IS_NOT_ALLOWED_EMPTY);
+		
+		if(isset($options[self::OSS_FILE_DOWNLOAD]) && $this->chk_chinese($options[self::OSS_FILE_DOWNLOAD])){
+			$options[self::OSS_FILE_DOWNLOAD] = iconv('utf-8','gbk',$options[self::OSS_FILE_DOWNLOAD]);
+		}
+
+		$options[self::OSS_BUCKET] = $bucket;
+		$options[self::OSS_METHOD] = 'GET';
+		$options[self::OSS_OBJECT] = $object;
+		
+		if(isset($options['lastmodified'])){
+			$options[self::OSS_HEADERS][self::OSS_IF_MODIFIED_SINCE] = $options['lastmodified'];
+            unset($options['lastmodified']);
+		}
+		
+		if(isset($options['etag'])){
+			$options[self::OSS_HEADERS][self::OSS_IF_NONE_MATCH] = $options['etag'];
+            unset($options['etag']);
+		}
+		
+		if(isset($options['range'])){
+			$options[self::OSS_HEADERS][self::OSS_RANGE] = 'bytes=' . $options['range'];
+            unset($options['range']);
+		}
+		
+		return $this->auth ( $options );
+	}
+
+	/**
+	 * 检测Object是否存在
+	 * @param string $bucket(Required)
+	 * @param string $object (Required)
+	 * @param array $options (Optional)
+	 * @author xiaobing.meng@alibaba-inc.com
+	 * @since 2011-11-14
+	 * @return boolean
+	 */
+	public function is_object_exist($bucket,$object,$options = NULL){
+		//options
+		$this->validate_options($options);
+
+		if(!$options){
+			$options = array();
+		}
+
+		//bucket
+		$this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY);
+
+		//object
+		$this->is_empty($object,OSS_OBJECT_IS_NOT_ALLOWED_EMPTY);
+
+		$options[self::OSS_BUCKET] = $bucket;
+		$options[self::OSS_METHOD] = 'GET';
+		$options[self::OSS_OBJECT] = $object;
+
+		$response = $this->get_object_meta($bucket, $object,$options);
+
+		return $response;
+	}
+
+
+	/*%******************************************************************************************************%*/
+	//Multi Part相关操作	
+	
+	/**
+	 * 计算文件可以分成多少个part,以及每个part的长度以及起始位置
+	 * 方法必须在 <upload_part()>中调用
+	 *
+	 * @param integer $filesize (Required) 文件大小
+	 * @param integer $part_size (Required) part大小,默认5M
+	 * @return array An array 包含 key-value 键值对. Key 为 `seekTo` 和 `length`.
+	 */	
+	public function get_multipart_counts($filesize, $part_size = 5242880 ){
+		$i = 0;
+		$sizecount = $filesize;
+		$values = array();
+
+		if((integer)$part_size <= 5242880){ 
+			$part_size = 5242880;	//5M
+		}elseif ((integer)$part_size > 524288000){
+			$part_size = 524288000; //500M
+		}else{
+			$part_size = 52428800; //50M
+		}		
+		
+		while ($sizecount > 0)
+		{
+			$sizecount -= $part_size;
+			$values[] = array(
+				self::OSS_SEEK_TO => ($part_size * $i),
+				self::OSS_LENGTH => (($sizecount > 0) ? $part_size : ($sizecount + $part_size)),
+			);
+			$i++;
+		}
+
+		return $values;		
+	}
+	
+	/**
+	 * 初始化multi-part upload,并且返回uploadId
+	 * @param string $bucket (Required) Bucket名称
+	 * @param string $object (Required) Object名称
+	 * @param array $options (Optional) Key-Value数组,其中可以包括以下的key
+	 * @return ResponseCore
+	 */
+	public function initiate_multipart_upload($bucket,$object,$options = NULL){
+		//options
+		$this->validate_options($options);
+
+		if(!$options){
+			$options = array();
+		}
+
+		//bucket
+		$this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY);
+
+		//object
+		$this->is_empty($object,OSS_OBJECT_IS_NOT_ALLOWED_EMPTY);
+
+		// 发送请求
+		$options[self::OSS_METHOD] = self::OSS_HTTP_POST;
+		$options[self::OSS_BUCKET] = $bucket;
+		$options[self::OSS_OBJECT] = $object;
+		$options[self::OSS_SUB_RESOURCE] = 'uploads';
+		$options[self::OSS_CONTENT] = '';
+		//$options[self::OSS_CONTENT_LENGTH] = 0;
+		$options[self::OSS_HEADERS] = array(self::OSS_CONTENT_TYPE => 'application/octet-stream');
+
+		$response = $this->auth ( $options );
+
+		return $response;		
+	}
+	
+	/**
+	 * 上传part
+	 * @param string $bucket (Required) Bucket名称
+	 * @param string $object (Required) Object名称
+	 * @param string $upload_id (Required) uploadId
+	 * @param array $options (Optional) Key-Value数组,其中可以包括以下的key
+	 * @return ResponseCore
+	 */
+	public function upload_part($bucket, $object, $upload_id, $options = null){
+		//options
+		$this->validate_options($options);
+
+		if(!$options){
+			$options = array();
+		}
+
+		//bucket
+		$this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY);
+
+		//object
+		$this->is_empty($object,OSS_OBJECT_IS_NOT_ALLOWED_EMPTY);
+
+		if (!isset($options[self::OSS_FILE_UPLOAD]) || !isset($options['partNumber'])){
+			throw new OSS_Exception('The `fileUpload` and `partNumber` options are both required in ' . __FUNCTION__ . '().');
+		}
+						
+		$options[self::OSS_METHOD] = self::OSS_HTTP_PUT;
+		$options[self::OSS_BUCKET] = $bucket;
+		$options[self::OSS_OBJECT] = $object;
+		$options[self::OSS_UPLOAD_ID] = $upload_id;	
+		
+		if(isset($options[self::OSS_LENGTH])){
+			$options[self::OSS_CONTENT_LENGTH] =  $options[self::OSS_LENGTH];
+		}
+
+		return $this->auth($options);
+	}
+	
+	/**
+	 * list part
+	 * @param string $bucket (Required) Bucket名称
+	 * @param string $object (Required) Object名称
+	 * @param string $upload_id (Required) uploadId
+	 * @param array $options (Optional) Key-Value数组,其中可以包括以下的key
+	 * @return ResponseCore
+	 */	
+	public function list_parts($bucket, $object, $upload_id, $options = null){
+		//options
+		$this->validate_options($options);
+
+		if(!$options){
+			$options = array();
+		}
+
+		//bucket
+		$this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY);
+
+		//object
+		$this->is_empty($object,OSS_OBJECT_IS_NOT_ALLOWED_EMPTY);
+
+		$options[self::OSS_METHOD] = self::OSS_HTTP_GET;
+		$options[self::OSS_BUCKET] = $bucket;
+		$options[self::OSS_OBJECT] = $object;
+		$options[self::OSS_UPLOAD_ID] = $upload_id;
+		$options[self::OSS_QUERY_STRING] = array();
+
+		foreach (array('max-parts', 'part-number-marker') as $param){
+			if (isset($options[$param])){
+				$options[self::OSS_QUERY_STRING][$param] = $options[$param];
+				unset($options[$param]);
+			}
+		}	
+
+		return $this->auth($options);
+	}
+	
+	/**
+	 * 中止上传mulit-part upload
+	 * @param string $bucket (Required) Bucket名称
+	 * @param string $object (Required) Object名称
+	 * @param string $upload_id (Required) uploadId
+	 * @param array $options (Optional) Key-Value数组,其中可以包括以下的key
+	 * @return ResponseCore
+	 */	
+	public function abort_multipart_upload($bucket, $object, $upload_id, $options = NULL){
+		//options
+		$this->validate_options($options);
+
+		if(!$options){
+			$options = array();
+		}
+
+		//bucket
+		$this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY);
+
+		//object
+		$this->is_empty($object,OSS_OBJECT_IS_NOT_ALLOWED_EMPTY);
+
+		$options[self::OSS_METHOD] = self::OSS_HTTP_DELETE;
+		$options[self::OSS_BUCKET] = $bucket;
+		$options[self::OSS_OBJECT] = $object;
+		$options[self::OSS_UPLOAD_ID] = $upload_id;
+		
+		return $this->auth($options);
+	}
+	
+	/**
+	 * 完成multi-part上传
+	 * @param string $bucket (Required) Bucket名称
+	 * @param string $object (Required) Object名称
+	 * @param string $upload_id (Required) uploadId
+	 * @param string $parts xml格式文件
+	 * @param array $options (Optional) Key-Value数组,其中可以包括以下的key
+	 * @return ResponseCore
+	 */	
+	public function complete_multipart_upload($bucket, $object, $upload_id, $parts, $options = NULL){
+		//options
+		$this->validate_options($options);
+
+		if(!$options){
+			$options = array();
+		}
+
+		//bucket
+		$this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY);
+
+		//object
+		$this->is_empty($object,OSS_OBJECT_IS_NOT_ALLOWED_EMPTY);
+		
+		$options[self::OSS_METHOD] = self::OSS_HTTP_POST;
+		$options[self::OSS_BUCKET] = $bucket;
+		$options[self::OSS_OBJECT] = $object;
+		$options[self::OSS_UPLOAD_ID] = $upload_id;
+		$options[self::OSS_CONTENT_TYPE] = 'application/xml';
+
+		
+		if(is_string($parts)){
+			$options[self::OSS_CONTENT] = $parts;
+		}else if($parts instanceof SimpleXMLElement){
+			$options[self::OSS_CONTENT] = $parts->asXML();
+		}else if((is_array($parts) || $parts instanceof ResponseCore)){
+			$xml = new SimpleXMLElement('<?xml version="1.0" encoding="utf-8"?><CompleteMultipartUpload></CompleteMultipartUpload>');
+
+			if (is_array($parts)){
+				//生成关联的xml
+				foreach ($parts as $node){
+					$part = $xml->addChild('Part');
+					$part->addChild('PartNumber', $node['PartNumber']);
+					$part->addChild('ETag', $node['ETag']);
+				}
+			}elseif ($parts instanceof ResponseCore){
+				foreach ($parts->body->Part as $node){
+					$part = $xml->addChild('Part');
+					$part->addChild('PartNumber', (string) $node->PartNumber);
+					$part->addChild('ETag', (string) $node->ETag);
+				}
+			}
+
+			$options[self::OSS_CONTENT] = $xml->asXML();			
+		}
+	
+		return $this->auth($options);		
+	}
+	
+	/**
+	 * 列出multipart上传
+	 * @param string $bucket (Requeired) bucket 
+	 * @param array $options (Optional) 关联数组
+	 * @author xiaobing.meng@alibaba-inc.com
+	 * @since 2012-03-05
+	 * @return ResponseCore
+	 */
+	public function list_multipart_uploads($bucket, $options = null){
+		//options
+		$this->validate_options($options);
+
+		if(!$options){
+			$options = array();
+		}
+
+		//bucket
+		$this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY);
+
+		$options[self::OSS_METHOD] = self::OSS_HTTP_GET;
+		$options[self::OSS_BUCKET] = $bucket;
+		$options[self::OSS_OBJECT] = '/';
+		$options[self::OSS_SUB_RESOURCE] = 'uploads';
+
+		foreach (array('key-marker', 'max-uploads', 'upload-id-marker') as $param){
+			if (isset($options[$param])){
+				$options[self::OSS_QUERY_STRING][$param] = $options[$param];
+				unset($options[$param]);
+			}
+		}
+				
+		return $this->auth($options);
+	}
+	
+	/**
+	 * multipart上传统一封装,从初始化到完成multipart,以及出错后中止动作
+	 * @param unknown_type $bucket
+	 * @param unknown_type $object
+	 * @param unknown_type $options
+	 * @author xiaobing.meng@alibaba-inc.com
+	 * @since 2012-03-05
+	 * @return ResponseCore 
+	 */
+	public function create_mpu_object($bucket, $object, $options = null){
+		//options
+		$this->validate_options($options);
+
+		if(!$options){
+			$options = array();
+		}
+
+		//bucket
+		$this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY);
+
+		//object
+		$this->is_empty($object,OSS_OBJECT_IS_NOT_ALLOWED_EMPTY);
+
+		if(isset($options[self::OSS_LENGTH])){
+			$options[self::OSS_CONTENT_LENGTH] = $options[self::OSS_LENGTH];
+			unset($options[self::OSS_LENGTH]);
+		}
+		
+		if(isset($options[self::OSS_FILE_UPLOAD])){
+			if($this->chk_chinese($options[self::OSS_FILE_UPLOAD])){
+				$options[self::OSS_FILE_UPLOAD] = mb_convert_encoding($options[self::OSS_FILE_UPLOAD],'UTF-8');
+			}			
+		}
+		
+		if(!isset($options[self::OSS_FILE_UPLOAD])){
+			throw new OSS_Exception('The `fileUpload` option is required in ' . __FUNCTION__ . '().');
+		}elseif (is_resource($options[self::OSS_FILE_UPLOAD])){					
+			$upload_position = isset($options[self::OSS_SEEK_TO]) ? (integer) $options[self::OSS_SEEK_TO] : ftell($options[self::OSS_FILE_UPLOAD]);
+			$upload_filesize = isset($options[self::OSS_CONTENT_LENGTH]) ? (integer) $options[self::OSS_CONTENT_LENGTH] : null;
+
+			if (!isset($upload_filesize) && $upload_position !== false){
+				$stats = fstat($options[self::OSS_FILE_UPLOAD]);
+
+				if ($stats && $stats[self::OSS_SIZE] >= 0){
+					$upload_filesize = $stats[self::OSS_SIZE] - $upload_position;
+				}
+			}			
+		}else{
+			$upload_position = isset($options[self::OSS_SEEK_TO]) ? (integer) $options[self::OSS_SEEK_TO] : 0;
+
+			if (isset($options[self::OSS_CONTENT_TYPE])){
+				$upload_filesize = (integer) $options[self::OSS_CONTENT_TYPE];
+			}
+			else{
+				$upload_filesize = filesize($options[self::OSS_FILE_UPLOAD]);
+
+				if ($upload_filesize !== false){
+					$upload_filesize -= $upload_position;
+				}
+			}			
+		}
+		
+		if ($upload_position === false || !isset($upload_filesize) || $upload_filesize === false || $upload_filesize < 0){
+			throw new OSS_Exception('The size of `fileUpload` cannot be determined in ' . __FUNCTION__ . '().');
+		}
+		
+		// 处理partSize
+		if (isset($options[self::OSS_PART_SIZE])){
+			// 小于5M
+			if ((integer) $options[self::OSS_PART_SIZE] <= 5242880){
+				$options[self::OSS_PART_SIZE] = 5242880; // 5 MB
+			}
+			// 大于500M
+			elseif ((integer) $options[self::OSS_PART_SIZE] > 524288000){
+				$options[self::OSS_PART_SIZE] = 524288000; // 500 MB
+			}
+		}
+		else{
+			$options[self::OSS_PART_SIZE] = 52428800; // 50 MB
+		}
+
+		// 如果上传的文件小于partSize,则直接使用普通方式上传
+		if ($upload_filesize < $options[self::OSS_PART_SIZE] && !isset($options['uploadId'])){
+			return $this->upload_file_by_file($bucket, $object, $options[self::OSS_FILE_UPLOAD]);
+		}	
+
+		// 初始化multipart
+		if (isset($options['uploadId'])){
+			$upload_id = $options['uploadId'];
+		}else{
+			//初始化
+			$upload = $this->initiate_multipart_upload($bucket, $object);
+			
+			if (!$upload->isOK()){
+				throw new OSS_Exception('Init multi-part upload failed...');
+			}
+			$xml = new SimpleXmlIterator($upload->body);
+			$uploadId = (string)$xml->UploadId;
+		}		
+
+		// 或的分片
+		$pieces = $this->get_multipart_counts($upload_filesize, (integer) $options[self::OSS_PART_SIZE]);
+
+		$response_upload_part = array();
+		foreach ($pieces as $i => $piece){
+			$response_upload_part[] = $this->upload_part($bucket, $object, $uploadId, array(
+				//'expect' => '100-continue',
+				self::OSS_FILE_UPLOAD => $options[self::OSS_FILE_UPLOAD],
+				'partNumber' => ($i + 1),
+				self::OSS_SEEK_TO => $upload_position + (integer) $piece[self::OSS_SEEK_TO],
+				self::OSS_LENGTH => (integer) $piece[self::OSS_LENGTH],
+			));
+		}
+		
+		$upload_parts = array();
+		$upload_part_result = true;
+		
+		foreach ($response_upload_part as $i=>$response){
+			$upload_part_result = $upload_part_result && $response->isOk();
+		}
+		
+		if(!$upload_part_result){
+			throw new OSS_Exception('any part upload failed...,pls try again');
+		}
+		
+		foreach ($response_upload_part as $i=>$response){
+			$upload_parts[] = array(
+				'PartNumber' => ($i + 1),
+			    'ETag' => (string) $response->header['etag']
+			);		
+		}
+				
+		return $this->complete_multipart_upload($bucket, $object, $uploadId, $upload_parts);
+	}
+	
+	
+	/**
+	 * 通过Multi-Part方式上传整个目录,其中的object默认为文件名
+	 * @param string $bucket (Required) 
+	 * @param string $dir  (Required) 必选
+	 * @param boolean $recursive (Optional) 是否递归,如果为true,则递归读取所有目录,默认为不递归读取
+	 * @param string $exclude 需要过滤的文件
+	 * @param array $options (Optional) 关联数组
+	 * @author xiaobing.meng@alibaba-inc.com
+	 * @since 2012-03-05
+	 * @return ResponseCore 
+	 */
+	public function create_mtu_object_by_dir($bucket,$dir,$recursive = false,$exclude = ".|..|.svn",$options = null){
+		//options
+		$this->validate_options($options);
+
+		//bucket
+		$this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY);
+
+		if($this->chk_chinese($dir)){
+			$dir = iconv('utf-8','gbk',$dir);
+		}
+		
+		//判断是否目录
+		if(!is_dir($dir)){
+			throw new OSS_Exception($dir.' is not a directory...,pls check it');
+		}
+		
+		$file_list_array = $this->read_dir($dir,$exclude,$recursive);
+				
+		if(!$file_list_array){
+			throw new OSS_Exception($dir.' is empty...');
+		}
+		
+		$index = 1;
+		
+		foreach ($file_list_array as $item){
+			$options = array(
+				self::OSS_FILE_UPLOAD => $item['path'],
+				self::OSS_PART_SIZE => 5242880,
+			);			
+			
+			echo $index++.". ";
+			$response = $this->create_mpu_object($bucket, $item['file'],$options);
+			if($response->isOK()){
+				echo "Upload file {".$item['path']." } successful..\n";
+			}else{
+				echo "Upload file {".$item['path']." } failed..\n";
+				continue;
+			}
+		}
+	}
+	
+	/**
+	 * 通过multi-part方式上传目录(优化版)
+	 * $options = array(
+	 * 		'bucket' 	=>  (Required)
+	 * 		'object' 	=>  (Optional)
+	 * 		'directory' =>  (Required)
+	 * 		'exclude'	=>  (Optional)
+	 * 		'recursive' =>  (Optional)
+	 * )
+	 */
+	public function batch_upload_file($options = NULL){
+		if((NULL == $options) || !isset($options['bucket']) || empty($options['bucket']) || !isset($options['directory']) ||empty($options['directory']) ) {
+			throw new OSS_Exception('Bad Request',400);
+		}
+		
+		$bucket = $options['bucket']; unset($options['bucket']);
+		
+		$directory = $options['directory']; unset($options['directory']);
+		if($this->chk_chinese($directory)){
+			$directory = iconv('utf-8','gbk',$directory);
+		}		
+		
+		//判断是否目录
+		if(!is_dir($directory)){
+			throw new OSS_Exception($dir.' is not a directory...,pls check it');
+		}
+				
+		$object = '';
+		if(isset($options['object'])){
+			$object = $options['object'];
+			unset($options['object']);	
+		}
+		
+		$exclude = '.|..|.svn';
+		if (isset($options['exclude']) && !empty($options['exclude'])){
+			$exclude = $options['exclude'];
+			unset($options['exclude']);
+		}
+		
+		$recursive = false;
+		if(isset($options['recursive']) && !empty($options['recursive'])){
+			if(in_array($options['recursive'],array(true,false))){
+				$recursive = $options['recursive'];
+			}
+			unset($options['recursive']);
+		}
+		
+		//read directory
+		$file_list_array = $this->read_dir($directory,$exclude,$recursive);		
+		
+		if(!$file_list_array){
+			throw new OSS_Exception($directory.' is empty...');
+		}
+		
+		$index = 1;
+		
+		foreach ($file_list_array as $item){
+			$options = array(
+				self::OSS_FILE_UPLOAD => $item['path'],
+				self::OSS_PART_SIZE => 5242880,
+			);			
+			
+			echo $index++.". ";
+			$response = $this->create_mpu_object($bucket, (!empty($object)?$object.'/':'').$item['file'],$options);
+			if($response->isOK()){
+				echo "Upload file {".$item['path']." } successful..\n";
+			}else{
+				echo "Upload file {".$item['path']." } failed..\n";
+				continue;
+			}
+		}		
+	}
+	
+		
+	/*%******************************************************************************************************%*/
+	//Object Group相关操作
+
+	/**
+	 * 创建Object Group
+	 * @param string $object_group (Required)  Object Group名称
+	 * @param string $bucket (Required) Bucket名称
+	 * @param array $object_arry (Required) object数组,所有的object必须在同一个bucket下
+	 * 其中$object 数组的格式如下:
+	 * $object = array(
+	 * 		$object1,
+	 * 		$object2,
+	 * 		...
+	 * )
+	 * @param array $options (Optional)
+	 * @author xiaobing.meng@alibaba-inc.com
+	 * @since 2011-11-14
+	 * @return ResponseCore
+	 */
+	public function create_object_group($bucket,$object_group  ,$object_arry,$options = NULL){
+		//options
+		$this->validate_options($options);
+
+		//bucket
+		$this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY);
+
+		//object group
+		$this->is_empty($object_group,OSS_OBJECT_GROUP_IS_NOT_ALLOWED_EMPTY);
+
+		$options[self::OSS_BUCKET] = $bucket;
+		$options[self::OSS_METHOD] = 'POST';
+		$options[self::OSS_OBJECT] = $object_group;
+		$options[self::OSS_CONTENT_TYPE] = 'txt/xml';  //重设Content-Type
+		$options[self::OSS_SUB_RESOURCE] = 'group';	   //设置?group
+		$options[self::OSS_CONTENT] = $this->make_object_group_xml($bucket,$object_arry);   //格式化xml
+
+		$response = $this->auth ( $options );
+
+		return $response;
+	}
+
+	/**
+	 * 获取Object Group
+	 * @param string $object_group (Required)
+	 * @param string $bucket	(Required)
+	 * @param array $options	(Optional)
+	 * @author xiaobing.meng@alibaba-inc.com
+	 * @since 2011-11-14
+	 * @return ResponseCore
+	 */
+	public function get_object_group($bucket,$object_group,$options = NULL){
+		//options
+		$this->validate_options($options);
+
+		//bucket
+		$this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY);
+
+		//object group
+		$this->is_empty($object_group,OSS_OBJECT_GROUP_IS_NOT_ALLOWED_EMPTY);
+
+		$options[self::OSS_BUCKET] = $bucket;
+		$options[self::OSS_METHOD] = 'GET';
+		$options[self::OSS_OBJECT] = $object_group;
+		//$options[self::OSS_OBJECT_GROUP] = true;	   //设置?group
+		//$options[self::OSS_CONTENT_TYPE] = 'txt/xml';  //重设Content-Type
+		$options[self::OSS_HEADERS] = array(self::OSS_OBJECT_GROUP => self::OSS_OBJECT_GROUP);  //header中的x-oss-file-group不能为空,否则返回值错误
+
+		$response = $this->auth ( $options );
+
+		return $response;
+	}
+
+	/**
+	 * 获取Object Group 的Object List信息
+	 * @param string $object_group (Required)
+	 * @param string $bucket	(Required)
+	 * @param array $options	(Optional)
+	 * @author xiaobing.meng@alibaba-inc.com
+	 * @since 2011-11-14
+	 * @return ResponseCore
+	 */
+	public function get_object_group_index($bucket,$object_group,$options = NULL){
+		//options
+		$this->validate_options($options);
+
+		//bucket
+		$this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY);
+
+		//object group
+		$this->is_empty($object_group,OSS_OBJECT_GROUP_IS_NOT_ALLOWED_EMPTY);
+
+		$options[self::OSS_BUCKET] = $bucket;
+		$options[self::OSS_METHOD] = 'GET';
+		$options[self::OSS_OBJECT] = $object_group;
+		$options[self::OSS_CONTENT_TYPE] = 'application/xml';  //重设Content-Type
+		//$options[self::OSS_OBJECT_GROUP] = true;	   //设置?group
+		$options[self::OSS_HEADERS] = array(self::OSS_OBJECT_GROUP => self::OSS_OBJECT_GROUP);
+
+		$response = $this->auth ( $options );
+
+		return $response;
+	}
+
+	/**
+	 * 获得object group的meta信息
+	 * @param string $bucket (Required)
+	 * @param string $object_group (Required)
+	 * @param string $options (Optional)
+	 * @author xiaobing.meng@alibaba-inc.com
+	 * @since 2011-11-14
+	 * @return ResponseCore
+	 */
+	public function get_object_group_meta($bucket,$object_group,$options = NULL){
+		//options
+		$this->validate_options($options);
+
+		if(!$options){
+			$options = array();
+		}
+
+		//bucket
+		$this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY);
+
+		//object group
+		$this->is_empty($object_group,OSS_OBJECT_IS_NOT_ALLOWED_EMPTY);
+
+		$options[self::OSS_BUCKET] = $bucket;
+		$options[self::OSS_METHOD] = 'HEAD';
+		$options[self::OSS_OBJECT] = $object_group;
+		$options[self::OSS_CONTENT_TYPE] = 'application/xml';  //重设Content-Type
+		//$options[self::OSS_SUB_RESOURCE] = 'group';	   //设置?group
+		$options[self::OSS_HEADERS] = array(self::OSS_OBJECT_GROUP => self::OSS_OBJECT_GROUP);
+
+		$response = $this->auth ( $options );
+
+		return $response;
+	}
+
+	/**
+	 * 删除Object Group
+	 * @param string $bucket(Required)
+	 * @param string $object_group (Required)
+	 * @param array $options (Optional)
+	 * @author xiaobing.meng@alibaba-inc.com
+	 * @since 2011-11-14
+	 * @return ResponseCore
+	 */
+	public function delete_object_group($bucket,$object_group,$options = NULL){
+		//options
+		$this->validate_options($options);
+
+		if(!$options){
+			$options = array();
+		}
+
+		//bucket
+		$this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY);
+
+		//object group
+		$this->is_empty($object_group,OSS_OBJECT_IS_NOT_ALLOWED_EMPTY);
+
+		$options[self::OSS_BUCKET] = $bucket;
+		$options[self::OSS_METHOD] = 'DELETE';
+		$options[self::OSS_OBJECT] = $object_group;
+
+		$response = $this->auth ( $options );
+
+		return $response;
+	}
+
+
+	/*%******************************************************************************************************%*/
+	//带签名的url相关
+
+	/**
+	 * 获取带签名的url
+	 * @param string $bucket (Required)
+	 * @param string $object (Required)
+	 * @param int	 $timeout (Optional)
+	 * @param array $options (Optional)
+	 * @author xiaobing.meng@alibaba-inc.com
+	 * @since 2011-12-21
+	 * @return string
+	 */
+	public function get_sign_url($bucket,$object,$timeout = 60,$options = NULL){
+		//options
+		$this->validate_options($options);
+
+		if(!$options){
+			$options = array();
+		}
+
+		//bucket
+		$this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY);
+
+		//object
+		$this->is_empty($object,OSS_OBJECT_IS_NOT_ALLOWED_EMPTY);
+
+		$options[self::OSS_BUCKET] = $bucket;
+		$options[self::OSS_OBJECT] = $object;
+		$options[self::OSS_METHOD] = self::OSS_HTTP_GET;
+		$options[self::OSS_CONTENT_TYPE] = '';
+
+		$timeout = time() + $timeout;
+		$options[self::OSS_PREAUTH] = $timeout;
+		$options[self::OSS_DATE] = $timeout;
+
+		return $this->auth($options);
+	}
+
+	/*%******************************************************************************************************%*/
+	//日志相关
+
+	/**
+	 * 记录日志
+	 * @param string $msg (Required)
+	 * @throws OSS_Exception
+	 * @author xiaobing.meng@alibaba-inc.com
+	 * @since 2011-12-27
+	 * @return void
+	 */
+	private function log($msg){
+		if(defined('ALI_LOG_PATH') ){
+			$log_path = ALI_LOG_PATH;
+			if(empty($log_path) || !file_exists($log_path)){
+				throw new OSS_Exception($log_path.OSS_LOG_PATH_NOT_EXIST);
+			}
+		}else{
+			$log_path = dirname(__FILE__).DIRECTORY_SEPARATOR.'logs'.DIRECTORY_SEPARATOR;
+		}
+		
+		//检测日志目录是否存在
+		if(!file_exists($log_path)){
+			throw new OSS_Exception(OSS_LOG_PATH_NOT_EXIST);
+		}
+
+		$log_name = $log_path.'oss_sdk_php_'.date('Y-m-d').'.log';
+
+		if(ALI_DISPLAY_LOG){
+			echo $msg."\n<br/>";
+		}
+		
+		if(ALI_LOG){
+			if(!error_log(date('Y-m-d H:i:s')." : ".$msg."\n", 3,$log_name)){
+				throw new OSS_Exception(OSS_WRITE_LOG_TO_FILE_FAILED);
+			}
+		}
+	}
+
+
+	/*%******************************************************************************************************%*/
+	//工具类相关
+
+	/**
+	 * 生成query params
+	 * @param array $array 关联数组
+	 * @author xiaobing.meng@alibaba-inc.com
+	 * @since 2012-03-04
+	 * @return string 返回诸如 key1=value1&key2=value2
+	 */
+	public function to_query_string($options = array()){
+		$temp = array();
+		
+		foreach ($options as $key => $value){
+			if (is_string($key) && !is_array($value)){
+				$temp[] = rawurlencode($key) . '=' . rawurlencode($value);
+			}
+		}
+
+		return implode('&', $temp);
+	}
+	
+	/**
+	 * 转化十六进制的数据为base64
+	 *
+	 * @param string $str (Required) 要转化的字符串
+	 * @author xiaobing.meng@alibaba-inc.com
+	 * @since 2012-03-20
+	 * @return string Base64-encoded string.
+	 */
+	private function hex_to_base64($str){
+		$result = '';
+
+		for ($i = 0; $i < strlen($str); $i += 2){
+			$result .= chr(hexdec(substr($str, $i, 2)));
+		}
+
+		return base64_encode($result);
+	}
+
+	private function s_replace($subject){
+		$search = array('<','>','&','\'','"');
+		$replace = array('&lt;','&gt;','&amp;','&apos;','&quot;');
+		return str_replace($search, $replace, $subject);
+	}
+	
+	/**
+	 * 检测是否含有中文
+	 * @param string $subject
+	 * @author xiaobing.meng@alibaba-inc.com
+	 * @since 2012-06-06
+	 * @return boolean
+	 */
+	private function chk_chinese($str){
+		return preg_match('/[\x80-\xff]./', $str);
+	}
+	
+	/**
+	 * 检测是否GB2312编码
+	 * @param string $str 
+	 * @author xiaobing.meng@alibaba-inc.com
+	 * @since 2012-03-20
+	 * @return boolean false UTF-8编码  TRUE GB2312编码
+	 */
+	function is_gb2312($str)  {  
+	    for($i=0; $i<strlen($str); $i++) {  
+	        $v = ord( $str[$i] );  
+	        if( $v > 127) {  
+	            if( ($v >= 228) && ($v <= 233) ){  
+	                if( ($i+2) >= (strlen($str) - 1)) return true;  // not enough characters  
+	                $v1 = ord( $str[$i+1] );  
+	                $v2 = ord( $str[$i+2] );  
+	                if( ($v1 >= 128) && ($v1 <=191) && ($v2 >=128) && ($v2 <= 191) )  
+	                    return false;   //UTF-8编码  
+	                else  
+	                    return true;    //GB编码  
+	            }  
+	        }  
+	    }  
+	} 
+
+
+	/**
+	 * 检测是否GBK编码
+	 * @param string $str 
+	 * @param boolean $gbk
+	 * @author xiaobing.meng@alibaba-inc.com
+	 * @since 2012-06-04
+	 * @return boolean 
+	 */	
+	private function check_char($str,$gbk = true){ 
+	    for($i=0; $i<strlen($str); $i++) {
+	        $v = ord( $str[$i] );
+	        if( $v > 127){
+	            if( ($v >= 228) && ($v <= 233) ){
+	                 if(($i+2)>= (strlen($str)-1)) return $gbk?true:FALSE;  // not enough characters
+	                 $v1 = ord( $str[$i+1] ); $v2 = ord( $str[$i+2] );
+	                 if($gbk){
+	                      return (($v1 >= 128) && ($v1 <=191) && ($v2 >=128) && ($v2 <= 191))?FALSE:TRUE;//GBK
+	                 }else{
+	                      return (($v1 >= 128) && ($v1 <=191) && ($v2 >=128) && ($v2 <= 191))?TRUE:FALSE;
+	                 }
+	            }
+	        }
+	    }
+	   return $gbk?TRUE:FALSE;
+	}
+
+
+	/**
+	 * 读取目录
+	 * @param string $dir (Required) 目录名
+	 * @param boolean $recursive (Optional) 是否递归,默认为false
+	 * @author xiaobing.meng@alibaba-inc.com
+	 * @since 2012-03-05
+	 * @return array
+	 */
+	private  function read_dir($dir,$exclude = ".|..|.svn",$recursive = false){
+		static $file_list_array = array();
+		
+		$exclude_array = explode("|", $exclude);
+		//读取目录
+		if($handle = opendir($dir)){
+			while ( false !== ($file = readdir($handle))){						
+				if(!in_array(strtolower($file),$exclude_array)){
+					$new_file = $dir.'/'.$file;					
+					if(is_dir($new_file) && $recursive){
+						$this->read_dir($new_file,$exclude,$recursive);
+					}else{
+						$file_list_array[] = array(
+							'path' => $new_file,
+							'file' => $file,
+						);
+					}
+				}
+			}
+			
+			closedir($handle);			
+		}			
+		
+		return $file_list_array;
+	}
+	
+	
+	/**
+	 * 转化object数组为固定个xml格式
+	 * @param string $bucket (Required)
+	 * @param array $object_array (Required)
+	 * @throws OSS_Exception
+	 * @author xiaobing.meng@alibaba-inc.com
+	 * @since 2011-12-27
+	 * @return string
+	 */
+	private function make_object_group_xml($bucket,$object_array){
+		$xml = '';
+		$xml .= '<CreateFileGroup>';
+
+		if($object_array){
+			if(count($object_array) > self::OSS_MAX_OBJECT_GROUP_VALUE){
+				throw new OSS_Exception(OSS_OBJECT_GROUP_TOO_MANY_OBJECT, '-401');
+			}
+			$index = 1;
+			foreach ($object_array as $key=>$value){
+				$object_meta = (array)$this->get_object_meta($bucket, $value);
+				if(isset($object_meta) && isset($object_meta['status']) && isset($object_meta['header']) && isset($object_meta['header']['etag']) && $object_meta['status'] == 200){
+					$xml .= '<Part>';
+					$xml .= '<PartNumber>'.intval($index).'</PartNumber>';
+					$xml .= '<PartName>'.$value.'</PartName>';
+					$xml .= '<ETag>'.$object_meta['header']['etag'].'</ETag>';
+					$xml .= '</Part>';
+
+					$index++;
+				}
+			}
+		}else{
+			throw new OSS_Exception(OSS_OBJECT_ARRAY_IS_EMPTY, '-400');
+		}
+
+		$xml .= '</CreateFileGroup>';
+
+		return $xml;
+	}
+
+	/**
+	 * 检验bucket名称是否合法
+	 * bucket的命名规范:
+	 * 1. 只能包括小写字母,数字
+	 * 2. 必须以小写字母或者数字开头
+	 * 3. 长度必须在3-63字节之间
+	 * @param string $bucket (Required)
+	 * @author xiaobing.meng@alibaba-inc.com
+	 * @since 2011-12-27
+	 * @return boolean
+	 */
+	private function validate_bucket($bucket){
+		$pattern = '/^[a-z0-9][a-z0-9-]{2,62}$/';
+		if (! preg_match ( $pattern, $bucket )) {
+			return false;
+		}
+		return true;
+	}
+
+	/**
+	 * 检验object名称是否合法
+	 * object命名规范:
+	 * 1. 规则长度必须在1-1023字节之间
+	 * 2. 使用UTF-8编码
+	 * @param string $object (Required)
+	 * @author xiaobing.meng@alibaba-inc.com
+	 * @since 2011-12-27
+	 * @return boolean
+	 */
+	private function validate_object($object){
+		$pattern = '/^.{1,1023}$/';
+		if (empty ( $object ) || ! preg_match ( $pattern, $object )) {
+			return false;
+		}
+		return true;
+	}
+
+	/**
+	 * 检验$options
+	 * @param array $options (Optional)
+	 * @throws OSS_Exception
+	 * @author xiaobing.meng@alibaba-inc.com
+	 * @since 2011-12-27
+	 * @return boolean 
+	 */
+	private function validate_options($options){
+		//$options
+		if ($options != NULL && ! is_array ( $options )) {
+			throw new OSS_Exception ($options.':'.OSS_OPTIONS_MUST_BE_ARRAY);
+		}
+	}
+
+	/**
+	 * 检测上传文件的内容
+	 * @param array $options (Optional)
+	 * @throws OSS_Exception
+	 * @author xiaobing.meng@alibaba-inc.com
+	 * @since  2011-12-27
+	 * @return string
+	 */
+	private function validate_content($options){
+		if(isset($options[self::OSS_CONTENT])){
+			if($options[self::OSS_CONTENT] == '' || !is_string($options[self::OSS_CONTENT])){
+				throw new OSS_Exception(OSS_INVALID_HTTP_BODY_CONTENT,'-600');
+			}
+		}else{
+			throw new OSS_Exception(OSS_NOT_SET_HTTP_CONTENT, '-601');
+		}
+	}
+
+	/**
+	 * 验证文件长度
+	 * @param array $options (Optional)
+	 * @author xiaobing.meng@alibaba-inc.com
+	 * @since 2011-12-27
+	 * @return void
+	 */
+	private function validate_content_length($options){
+		if(isset($options[self::OSS_LENGTH]) && is_numeric($options[self::OSS_LENGTH])){
+			if( ! $options[self::OSS_LENGTH] > 0){
+				throw new OSS_Exception(OSS_CONTENT_LENGTH_MUST_MORE_THAN_ZERO, '-602');
+			}
+		}else{
+			throw new OSS_Exception(OSS_INVALID_CONTENT_LENGTH, '-602');
+		}
+	}
+
+	/**
+	 * 校验BUCKET/OBJECT/OBJECT GROUP是否为空
+	 * @param  string $name (Required)
+	 * @param  string $errMsg (Required)
+	 * @throws OSS_Exception
+	 * @author xiaobing.meng@alibaba-inc.com
+	 * @since 2011-12-27
+	 * @return void
+	 */
+	private function is_empty($name,$errMsg){
+		if(empty($name)){
+			throw new OSS_Exception($errMsg);
+		}
+	}
+
+	/**
+	 * 设置http header
+	 * @param string $key (Required)
+	 * @param string $value (Required)
+	 * @param array $options (Required)
+	 * @throws OSS_Exception
+	 * @author xiaobing.meng@alibaba-inc.com
+	 * @return void
+	 */
+	private static function set_options_header($key, $value, &$options) {
+		if (isset ( $options [self::OSS_HEADERS] )) {
+			if (! is_array ( $options [self::OSS_HEADERS] )) {
+				throw new OSS_Exception(OSS_INVALID_OPTION_HEADERS, '-600');
+			}
+		} else {
+			$options [self::OSS_HEADERS] = array ();
+		}
+
+		$options [self::OSS_HEADERS] [$key] = $value;
+	} 	
+}

+ 121 - 0
include/storages/oss/util/mimetypes.class.php

@@ -0,0 +1,121 @@
+<?php
+/**
+ * 获得文件的mime type类型
+ * @author xiaobing.meng
+ *
+ */
+class MimeTypes {
+	public static $mime_types = array (
+			'apk' => 'application/vnd.android.package-archive',
+			'3gp' => 'video/3gpp', 'ai' => 'application/postscript', 
+			'aif' => 'audio/x-aiff', 'aifc' => 'audio/x-aiff', 
+			'aiff' => 'audio/x-aiff', 'asc' => 'text/plain', 
+			'atom' => 'application/atom+xml', 'au' => 'audio/basic', 
+			'avi' => 'video/x-msvideo', 'bcpio' => 'application/x-bcpio', 
+			'bin' => 'application/octet-stream', 'bmp' => 'image/bmp', 
+			'cdf' => 'application/x-netcdf', 'cgm' => 'image/cgm', 
+			'class' => 'application/octet-stream', 
+			'cpio' => 'application/x-cpio', 
+			'cpt' => 'application/mac-compactpro', 
+			'csh' => 'application/x-csh', 'css' => 'text/css', 
+			'dcr' => 'application/x-director', 'dif' => 'video/x-dv', 
+			'dir' => 'application/x-director', 'djv' => 'image/vnd.djvu', 
+			'djvu' => 'image/vnd.djvu', 
+			'dll' => 'application/octet-stream', 
+			'dmg' => 'application/octet-stream', 
+			'dms' => 'application/octet-stream', 
+			'doc' => 'application/msword', 'dtd' => 'application/xml-dtd', 
+			'dv' => 'video/x-dv', 'dvi' => 'application/x-dvi', 
+			'dxr' => 'application/x-director', 
+			'eps' => 'application/postscript', 'etx' => 'text/x-setext', 
+			'exe' => 'application/octet-stream', 
+			'ez' => 'application/andrew-inset', 'flv' => 'video/x-flv', 
+			'gif' => 'image/gif', 'gram' => 'application/srgs', 
+			'grxml' => 'application/srgs+xml', 
+			'gtar' => 'application/x-gtar', 'gz' => 'application/x-gzip', 
+			'hdf' => 'application/x-hdf', 
+			'hqx' => 'application/mac-binhex40', 'htm' => 'text/html', 
+			'html' => 'text/html', 'ice' => 'x-conference/x-cooltalk', 
+			'ico' => 'image/x-icon', 'ics' => 'text/calendar', 
+			'ief' => 'image/ief', 'ifb' => 'text/calendar', 
+			'iges' => 'model/iges', 'igs' => 'model/iges', 
+			'jnlp' => 'application/x-java-jnlp-file', 'jp2' => 'image/jp2', 
+			'jpe' => 'image/jpeg', 'jpeg' => 'image/jpeg', 
+			'jpg' => 'image/jpeg', 'js' => 'application/x-javascript', 
+			'kar' => 'audio/midi', 'latex' => 'application/x-latex', 
+			'lha' => 'application/octet-stream', 
+			'lzh' => 'application/octet-stream', 
+			'm3u' => 'audio/x-mpegurl', 'm4a' => 'audio/mp4a-latm', 
+			'm4p' => 'audio/mp4a-latm', 'm4u' => 'video/vnd.mpegurl', 
+			'm4v' => 'video/x-m4v', 'mac' => 'image/x-macpaint', 
+			'man' => 'application/x-troff-man', 
+			'mathml' => 'application/mathml+xml', 
+			'me' => 'application/x-troff-me', 'mesh' => 'model/mesh', 
+			'mid' => 'audio/midi', 'midi' => 'audio/midi', 
+			'mif' => 'application/vnd.mif', 'mov' => 'video/quicktime', 
+			'movie' => 'video/x-sgi-movie', 'mp2' => 'audio/mpeg', 
+			'mp3' => 'audio/mpeg', 'mp4' => 'video/mp4', 
+			'mpe' => 'video/mpeg', 'mpeg' => 'video/mpeg', 
+			'mpg' => 'video/mpeg', 'mpga' => 'audio/mpeg', 
+			'ms' => 'application/x-troff-ms', 'msh' => 'model/mesh', 
+			'mxu' => 'video/vnd.mpegurl', 'nc' => 'application/x-netcdf', 
+			'oda' => 'application/oda', 'ogg' => 'application/ogg', 
+			'ogv' => 'video/ogv', 'pbm' => 'image/x-portable-bitmap', 
+			'pct' => 'image/pict', 'pdb' => 'chemical/x-pdb', 
+			'pdf' => 'application/pdf', 
+			'pgm' => 'image/x-portable-graymap', 
+			'pgn' => 'application/x-chess-pgn', 'pic' => 'image/pict', 
+			'pict' => 'image/pict', 'png' => 'image/png', 
+			'pnm' => 'image/x-portable-anymap', 
+			'pnt' => 'image/x-macpaint', 'pntg' => 'image/x-macpaint', 
+			'ppm' => 'image/x-portable-pixmap', 
+			'ppt' => 'application/vnd.ms-powerpoint', 
+			'ps' => 'application/postscript', 'qt' => 'video/quicktime', 
+			'qti' => 'image/x-quicktime', 'qtif' => 'image/x-quicktime', 
+			'ra' => 'audio/x-pn-realaudio', 
+			'ram' => 'audio/x-pn-realaudio', 'ras' => 'image/x-cmu-raster', 
+			'rdf' => 'application/rdf+xml', 'rgb' => 'image/x-rgb', 
+			'rm' => 'application/vnd.rn-realmedia', 
+			'roff' => 'application/x-troff', 'rtf' => 'text/rtf', 
+			'rtx' => 'text/richtext', 'sgm' => 'text/sgml', 
+			'sgml' => 'text/sgml', 'sh' => 'application/x-sh', 
+			'shar' => 'application/x-shar', 'silo' => 'model/mesh', 
+			'sit' => 'application/x-stuffit', 
+			'skd' => 'application/x-koan', 'skm' => 'application/x-koan', 
+			'skp' => 'application/x-koan', 'skt' => 'application/x-koan', 
+			'smi' => 'application/smil', 'smil' => 'application/smil', 
+			'snd' => 'audio/basic', 'so' => 'application/octet-stream', 
+			'spl' => 'application/x-futuresplash', 
+			'src' => 'application/x-wais-source', 
+			'sv4cpio' => 'application/x-sv4cpio', 
+			'sv4crc' => 'application/x-sv4crc', 'svg' => 'image/svg+xml', 
+			'swf' => 'application/x-shockwave-flash', 
+			't' => 'application/x-troff', 'tar' => 'application/x-tar', 
+			'tcl' => 'application/x-tcl', 'tex' => 'application/x-tex', 
+			'texi' => 'application/x-texinfo', 
+			'texinfo' => 'application/x-texinfo', 'tif' => 'image/tiff', 
+			'tiff' => 'image/tiff', 'tr' => 'application/x-troff', 
+			'tsv' => 'text/tab-separated-values', 'txt' => 'text/plain', 
+			'ustar' => 'application/x-ustar', 
+			'vcd' => 'application/x-cdlink', 'vrml' => 'model/vrml', 
+			'vxml' => 'application/voicexml+xml', 'wav' => 'audio/x-wav', 
+			'wbmp' => 'image/vnd.wap.wbmp', 
+			'wbxml' => 'application/vnd.wap.wbxml', 'webm' => 'video/webm', 
+			'wml' => 'text/vnd.wap.wml', 
+			'wmlc' => 'application/vnd.wap.wmlc', 
+			'wmls' => 'text/vnd.wap.wmlscript', 
+			'wmlsc' => 'application/vnd.wap.wmlscriptc', 
+			'wmv' => 'video/x-ms-wmv', 'wrl' => 'model/vrml', 
+			'xbm' => 'image/x-xbitmap', 'xht' => 'application/xhtml+xml', 
+			'xhtml' => 'application/xhtml+xml', 
+			'xls' => 'application/vnd.ms-excel', 
+			'xml' => 'application/xml', 'xpm' => 'image/x-xpixmap', 
+			'xsl' => 'application/xml', 'xslt' => 'application/xslt+xml', 
+			'xul' => 'application/vnd.mozilla.xul+xml', 
+			'xwd' => 'image/x-xwindowdump', 'xyz' => 'chemical/x-xyz', 
+			'zip' => 'application/zip' );
+	
+	public static function get_mimetype($ext) {
+		return (isset ( self::$mime_types [$ext] ) ? self::$mime_types [$ext] : 'application/octet-stream');
+	}
+}

+ 72 - 19
index.php

@@ -1,21 +1,12 @@
 <?php
-
-
-/**
- * 设置源静态文件的根目录的URL地址
- * */
-define('STATIC_URL','http://www.baidu.com/');
-
-/**
- * SAE storage的domain
- * */
-define('DOMAIN','cdn');
-
-/**
- * 空请求时是否显示文档
- * */
-define('WELCOME_DOC',TRUE);
-
+@ob_start();
+if(is_file('config.inc.php')){
+	require 'config.inc.php';
+}elseif(is_file('config.sample.inc.php')){
+	require 'config.sample.inc.php';
+}else{
+	die('Missing Config File.');
+}
 /**
  * 运行环境:development/testing/production
  * */
@@ -47,7 +38,69 @@ define('BASE_PATH',dirname(__FILE__).'/');
 
 define('BASE_URL', rtrim(STATIC_URL,'/').'/');
 
-define('IS_SAE', defined('SAE_SECRETKEY'));
+//自动判断环境
+if(!defined('RUN_ENV')){
+	if(defined('SAE_SECRETKEY')){
+		define('RUN_ENV','SAE');
+	}elseif(getenv('HTTP_BAE_ENV_SK')){
+		define('RUN_ENV','BAE');
+	}else{
+		define('RUN_ENV','LOCAL');
+	}
+}
+
+
+	global $PHP_SELF;
+
+	$default_server_values = array(
+		'SERVER_SOFTWARE' => '',
+		'REQUEST_URI' => '',
+	);
+
+	$_SERVER = array_merge( $default_server_values, $_SERVER );
+
+	// Fix for IIS when running with PHP ISAPI
+	if ( empty( $_SERVER['REQUEST_URI'] ) || ( php_sapi_name() != 'cgi-fcgi' && preg_match( '/^Microsoft-IIS\//', $_SERVER['SERVER_SOFTWARE'] ) ) ) {
+
+		// IIS Mod-Rewrite
+		if ( isset( $_SERVER['HTTP_X_ORIGINAL_URL'] ) ) {
+			$_SERVER['REQUEST_URI'] = $_SERVER['HTTP_X_ORIGINAL_URL'];
+		}
+		// IIS Isapi_Rewrite
+		else if ( isset( $_SERVER['HTTP_X_REWRITE_URL'] ) ) {
+			$_SERVER['REQUEST_URI'] = $_SERVER['HTTP_X_REWRITE_URL'];
+		} else {
+			// Use ORIG_PATH_INFO if there is no PATH_INFO
+			if ( !isset( $_SERVER['PATH_INFO'] ) && isset( $_SERVER['ORIG_PATH_INFO'] ) )
+				$_SERVER['PATH_INFO'] = $_SERVER['ORIG_PATH_INFO'];
+
+			// Some IIS + PHP configurations puts the script-name in the path-info (No need to append it twice)
+			if ( isset( $_SERVER['PATH_INFO'] ) ) {
+				if ( $_SERVER['PATH_INFO'] == $_SERVER['SCRIPT_NAME'] )
+					$_SERVER['REQUEST_URI'] = $_SERVER['PATH_INFO'];
+				else
+					$_SERVER['REQUEST_URI'] = $_SERVER['SCRIPT_NAME'] . $_SERVER['PATH_INFO'];
+			}
+
+			// Append the query string if it exists and isn't null
+			if ( ! empty( $_SERVER['QUERY_STRING'] ) ) {
+				$_SERVER['REQUEST_URI'] .= '?' . $_SERVER['QUERY_STRING'];
+			}
+		}
+	}
+
+	// Fix for PHP as CGI hosts that set SCRIPT_FILENAME to something ending in php.cgi for all requests
+	if ( isset( $_SERVER['SCRIPT_FILENAME'] ) && ( strpos( $_SERVER['SCRIPT_FILENAME'], 'php.cgi' ) == strlen( $_SERVER['SCRIPT_FILENAME'] ) - 7 ) )
+		$_SERVER['SCRIPT_FILENAME'] = $_SERVER['PATH_TRANSLATED'];
+
+	// Fix for Dreamhost and other PHP as CGI hosts
+	if ( strpos( $_SERVER['SCRIPT_NAME'], 'php.cgi' ) !== false )
+		unset( $_SERVER['PATH_INFO'] );
+
+	// Fix empty PHP_SELF
+	$PHP_SELF = $_SERVER['PHP_SELF'];
+	if ( empty( $PHP_SELF ) )
+		$_SERVER['PHP_SELF'] = $PHP_SELF = preg_replace( '/(\?.*)?$/', '', $_SERVER["REQUEST_URI"] );
 
-require_once BASE_PATH.'include/start.php';
 
+require_once BASE_PATH.'include/start.php';

+ 21 - 24
views/welcome.php

@@ -4,9 +4,9 @@
 <html lang="en"> 
   <head> 
     <meta charset="utf-8"> 
-    <title>SaeLayer CDN</title> 
+    <title>Layer CDN</title> 
     <meta name="description" content=""> 
-    <meta name="author" content=""> 
+    <meta name="author" content="best33.com"> 
     <style> 
 *{
     margin:  0;
@@ -88,20 +88,21 @@ h1.page-header{
   </head> 
   <body> 
     <div id="body"> 
-        <h1 class="page-header">SaeLayer CDN</h1> 
+        <h1 class="page-header">Layer CDN</h1> 
         <div id="content"> 
-            <blockquote>SaeLayer CDN已经成功安装!</blockquote>
+            <blockquote>Layer CDN已经成功安装!</blockquote>
             <div class="unit"> 
-                <h2>关于SaeLayerCDN</h2> 
+                <h2>关于Layer</h2> 
                 <div class="description"> 
-                    <p>SaeLayerCDN是一个基于SAE的轻巧的CDN程序,你可以利用SaeLayerCDN快速地为你的博客或者网站搭建自己的CDN,从而加速网站的加载,提升用户体验。关于CDN,更多介绍在<a href="#">这里</a>。</p> 
-                    <p>SaeLayerCDN的优势在于:</p> 
+                    <p>Layer是一个基于PHP的轻巧的CDN程序,你可以利用Layer快速地为你的博客或者网站搭建自己的CDN,从而加速网站的加载,提升用户体验。Layer由SaeLayerCDN修改而来。</p> 
+                    <p>相比SaeLayerCDN,新版Layer的优势在于:</p> 
                     <ul> 
-                        <li>基于SAE云平台,充分利用其强大的分布式架构,CDN加速效果甚至好于大部分的专业、收费的CDN。程序免费开源。</li> 
-                        <li>一次修改,永久适用。</li> 
-                        <li>你可以不会编程也能够为你的网站添加CDN,添加SaeLayerCDN不用修改网站的核心程序。</li> 
-                        <li>如果不想使用CDN,能很容易就能改回来,而且数据仍在自己网站的服务器上。</li> 
-                        <li>SaeLayerCDN架构超轻量,全部代码只有200余行,非常便于自己修改和定制。</li> 
+                        <li>基于SAE/BAE以及本地可写PHP环境,程序免费开源。</li>
+        				<li>可扩展性高,可轻易移植到任何云平台。</li>
+                        <li>防盗链保护,更省流量。</li>
+                        <li>URL刷新缓存,轻松维护。</li>
+                        <li>自定义HTTP Host、自定义缓存后缀,极端特殊情况也能轻松使用,方便进行全站CDN。</li> 
+                        <li>内存缓存,更高效,更易管理。(开发中)</li> 
                     </ul> 
                 </div> 
             </div> 
@@ -109,8 +110,8 @@ h1.page-header{
                 <h2>安装和使用</h2> 
                 <div class="description"> 
                     <ul> 
-                        <li><strong>部署代码:</strong>可以手动部署(即创建应用、上传代码)或者在线安装<span class="small">(还没有通过审核o(︶︿︶)o)</span>。</li> 
-                        <li><strong>配置程序:</strong>如果是手动部署,则要先在SAE后台创建一个storage的domain。然后设置index.php,修改下面的四个常量
+                        <li><strong>部署代码:</strong>可以上传代码包或通过git/svn部署。</li> 
+                        <li><strong>配置程序:</strong>如果是手动部署,则要先在SAE后台创建一个storage的domain,或者在<a href="http://developer.baidu.com/bae/bcs/bucket/">BAE后台</a>创建一个bucket,然后右键点击bucket设为公开读属性。然后设置index.php,修改下面的三个常量即可使用(其它特性请自行设置,有详细注解)
 <?php
 $code = <<<CODE
 <?php
@@ -120,7 +121,7 @@ $code = <<<CODE
 define('STATIC_URL','http://www.baidu.com/');
 
 /**
- * SAE storage的domain
+ * SAE storage的domain(或BAE的bucket)
  * */
 define('DOMAIN','cdn');
 
@@ -129,14 +130,10 @@ define('DOMAIN','cdn');
  * */
 define('WELCOME_DOC',TRUE);
 
-/**
- * 运行环境:development/testing/production
- * */
-define('ENVIRONMENT','production');
 CODE;
 highlight_string($code);
-?></li> 
-                        <li><strong>使用CDN:</strong>将网站模板中静态文件的的根目录URL替换成你的SAE应用地址,如将<strong>http://blog.creatist.cn/</strong>logo.jpg换成<strong>http://mysqecdn.sinaapp.com/</strong>logo.jpg。<span class="small">对于网站程序的一个建议是,设置一个$cdn_base配置变量,然后静态文件的URL根据$cdn_base生成,以后修改CDN只要配置这个变量就可以了。</span></li> 
+?></li>
+                        <li><strong>使用CDN:</strong>将网站模板中静态文件的的根目录URL替换成你的SAE/BAE/PHP应用地址,如将<strong>http://blog.creatist.cn/</strong>logo.jpg换成<strong>http://mysqecdn.sinaapp.com/</strong>logo.jpg。<span class="small">对于网站程序的一个建议是,设置一个$cdn_base配置变量,然后静态文件的URL根据$cdn_base生成,以后修改CDN只要配置这个变量就可以了。</span></li> 
                         <li>最后,刷新你的网站。然后感受网站加载速度的飞跃。</li> 
                     </ul> 
                     <p></p> 
@@ -145,14 +142,14 @@ highlight_string($code);
             <div class="unit"> 
                 <h2>原理</h2> 
                 <div class="description"> 
-                    <p>大致就是取静态内容的过程中增加一个SAE层:前端从SAE取静态文件,当该文件是第一次被访问时,SAE从源服务器上取文件并保存到自己的storage里,然后返回给前端;之后就直接从SAE取而不需要再访问源服务器了。这样的好处就是在源服务器端可以实现无痛切换,不用使用像又拍CDN之类的API进行专门的编程,用户资源仍保存在源服务器上,只要将静态资源的前缀改为SAE的网址就行了,当不想使用CDN时可以再改回来。</p><p>例如,源文件地址是http://www.creatist.cn/avatar/21223.jpg(或者相对地址/avatar/21223.jpg),前端使用CDN,访问地址改成http://cdnxxx.sinaapp.com/avatar/21223.jpg(这通常是非常容易修改的)。SAE首先检查/avatar/21223.jpg在storage中是否已存在,如果存在即以前已经被访问过的话就直接取出来;如果不存在就从http://www.creatist.cn/avatar/21223.jpg保存到storage,以后就直接从storage里面取了。</p><p>SAE层还能实现其他很多功能,例如设置浏览器缓存、防盗链等等。</p> 
+                    <p>大致就是取静态内容的过程中增加一个CDN层:前端从CDN取静态文件,当该文件是第一次被访问时,CDN从源服务器上取文件并保存到自己的storage里,然后返回给前端;之后就直接从CDN取而不需要再访问源服务器了。这样的好处就是在源服务器端可以实现无痛切换,不用使用像又拍CDN之类的API进行专门的编程,用户资源仍保存在源服务器上,只要将静态资源的前缀改为CDN的网址就行了,当不想使用CDN时可以再改回来。</p><p>例如,源文件地址是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里面取了。</p><p>CDN层还能实现其他很多功能,例如设置浏览器缓存、防盗链等等。</p> 
                 </div> 
             </div> 
             <div class="unit"> 
                 <h2>其他</h2> 
                 <div class="description"> 
-                    <p>SaeLayerCDN还在完善中,项目的Github地址是:<a href="https://github.com/Slacken/cdn">https://github.com/Slacken/cdn</a>,欢迎fork和贡献代码。</p> 
-                    <p>的博客是:<a href="http://blog.creatist.cn/">http://blog.creatist.cn/</a></p> 
+                    <p>Layer还在完善中,项目的Github地址是:<a href="https://github.com/oott123/Layer">https://github.com/oott123/Layer</a>,欢迎fork和贡献代码。</p> 
+                    <p>SaeLayerCDN的作者的博客是:<a href="http://blog.creatist.cn/">http://blog.creatist.cn/</a></p> 
                     <p><a href="http://sae.sina.com.cn/"><img src="http://static.sae.sina.com.cn/image/poweredby/120X33_transparent.gif" width="120" height="33" /></a></p> 
                 </div> 
             </div>