소스 검색

新增图片上传功能

star7th 10 년 전
부모
커밋
4ae7c49f60

+ 18 - 1
Application/Home/Controller/PageController.class.php

@@ -149,7 +149,24 @@ class PageController extends BaseController {
 
     }
 
-
+    //上传图片
+    public function uploadImg(){
+        $upload = new \Think\Upload();// 实例化上传类
+        $upload->maxSize  = 3145728 ;// 设置附件上传大小
+        $upload->allowExts  = array('jpg', 'gif', 'png', 'jpeg');// 设置附件上传类型
+        $upload->rootPath = './Public/Uploads/';// 设置附件上传目录
+        $upload->savePath = '';// 设置附件上传子目录
+        $info = $upload->upload() ;
+        if(!$info) {// 上传错误提示错误信息
+          $this->error($upload->getError());
+          $this->sendError(10101);
+          return;
+        }else{// 上传成功 获取上传文件信息
+          
+          $url = get_domain().__ROOT__.$upload->rootPath.$info['editormd-image-file']['savepath'].$info['editormd-image-file']['savename'] ;
+          echo json_encode(array("url"=>$url,"success"=>1));
+        }
+    }
 
 
 }

+ 2 - 0
Application/Home/View/Page/edit.html

@@ -67,4 +67,6 @@
 
  <include file="Common/footer" />
  <script src="__PUBLIC__/editor.md/editormd.min.js"></script>
+ <script src="__PUBLIC__/editor.md/plugins/image-dialog/image-dialog.js"></script>
+ <script src="__PUBLIC__/editor.md/plugins/link-dialog/link-dialog.js"></script>
  <script src="__PUBLIC__/js/page/edit.js"></script>

+ 1 - 0
Public/Uploads/index.html

@@ -0,0 +1 @@
+ 

+ 1 - 1
Public/js/page/edit.js

@@ -40,7 +40,7 @@ $(function() {
       placeholder : "本编辑器支持Markdown编辑,左边编写,右边预览",
       imageUpload : true,
       imageFormats : ["jpg", "jpeg", "gif", "png", "bmp", "webp"],
-      imageUploadURL : "ImgUpload",
+      imageUploadURL : "uploadImg",
 
   });
 

+ 1 - 1
README.md

@@ -71,7 +71,7 @@ ShowDoc就是一个非常适合IT团队的在线文档分享工具,它可以
 
 - 目录权限
 
-	请确保Application/Runtime 有可写权限
+	请确保Application/Runtime 和Public/Uploads 有可写权限
 
 ###使用在线的ShowDoc
 

+ 429 - 0
ThinkPHP/Library/Think/Upload.class.php

@@ -0,0 +1,429 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2014 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: 麦当苗儿 <zuojiazi@vip.qq.com> <http://www.zjzit.cn>
+// +----------------------------------------------------------------------
+namespace Think;
+class Upload {
+    /**
+     * 默认上传配置
+     * @var array
+     */
+    private $config = array(
+        'mimes'         =>  array(), //允许上传的文件MiMe类型
+        'maxSize'       =>  0, //上传的文件大小限制 (0-不做限制)
+        'exts'          =>  array(), //允许上传的文件后缀
+        'autoSub'       =>  true, //自动子目录保存文件
+        'subName'       =>  array('date', 'Y-m-d'), //子目录创建方式,[0]-函数名,[1]-参数,多个参数使用数组
+        'rootPath'      =>  './Uploads/', //保存根路径
+        'savePath'      =>  '', //保存路径
+        'saveName'      =>  array('uniqid', ''), //上传文件命名规则,[0]-函数名,[1]-参数,多个参数使用数组
+        'saveExt'       =>  '', //文件保存后缀,空则使用原后缀
+        'replace'       =>  false, //存在同名是否覆盖
+        'hash'          =>  true, //是否生成hash编码
+        'callback'      =>  false, //检测文件是否存在回调,如果存在返回文件信息数组
+        'driver'        =>  '', // 文件上传驱动
+        'driverConfig'  =>  array(), // 上传驱动配置
+    );
+
+    /**
+     * 上传错误信息
+     * @var string
+     */
+    private $error = ''; //上传错误信息
+
+    /**
+     * 上传驱动实例
+     * @var Object
+     */
+    private $uploader;
+
+    /**
+     * 构造方法,用于构造上传实例
+     * @param array  $config 配置
+     * @param string $driver 要使用的上传驱动 LOCAL-本地上传驱动,FTP-FTP上传驱动
+     */
+    public function __construct($config = array(), $driver = '', $driverConfig = null){
+        /* 获取配置 */
+        $this->config   =   array_merge($this->config, $config);
+
+        /* 设置上传驱动 */
+        $this->setDriver($driver, $driverConfig);
+
+        /* 调整配置,把字符串配置参数转换为数组 */
+        if(!empty($this->config['mimes'])){
+            if(is_string($this->mimes)) {
+                $this->config['mimes'] = explode(',', $this->mimes);
+            }
+            $this->config['mimes'] = array_map('strtolower', $this->mimes);
+        }
+        if(!empty($this->config['exts'])){
+            if (is_string($this->exts)){
+                $this->config['exts'] = explode(',', $this->exts);
+            }
+            $this->config['exts'] = array_map('strtolower', $this->exts);
+        }
+    }
+
+    /**
+     * 使用 $this->name 获取配置
+     * @param  string $name 配置名称
+     * @return multitype    配置值
+     */
+    public function __get($name) {
+        return $this->config[$name];
+    }
+
+    public function __set($name,$value){
+        if(isset($this->config[$name])) {
+            $this->config[$name] = $value;
+            if($name == 'driverConfig'){
+                //改变驱动配置后重置上传驱动
+                //注意:必须选改变驱动然后再改变驱动配置
+                $this->setDriver(); 
+            }
+        }
+    }
+
+    public function __isset($name){
+        return isset($this->config[$name]);
+    }
+
+    /**
+     * 获取最后一次上传错误信息
+     * @return string 错误信息
+     */
+    public function getError(){
+        return $this->error;
+    }
+
+    /**
+     * 上传单个文件
+     * @param  array  $file 文件数组
+     * @return array        上传成功后的文件信息
+     */
+    public function uploadOne($file){
+        $info = $this->upload(array($file));
+        return $info ? $info[0] : $info;
+    }
+
+    /**
+     * 上传文件
+     * @param 文件信息数组 $files ,通常是 $_FILES数组
+     */
+    public function upload($files='') {
+        if('' === $files){
+            $files  =   $_FILES;
+        }
+        if(empty($files)){
+            $this->error = '没有上传的文件!';
+            return false;
+        }
+
+        /* 检测上传根目录 */
+        if(!$this->uploader->checkRootPath($this->rootPath)){
+            $this->error = $this->uploader->getError();
+            return false;
+        }
+
+        /* 检查上传目录 */
+        if(!$this->uploader->checkSavePath($this->savePath)){
+            $this->error = $this->uploader->getError();
+            return false;
+        }
+
+        /* 逐个检测并上传文件 */
+        $info    =  array();
+        if(function_exists('finfo_open')){
+            $finfo   =  finfo_open ( FILEINFO_MIME_TYPE );
+        }
+        // 对上传文件数组信息处理
+        $files   =  $this->dealFiles($files);    
+        foreach ($files as $key => $file) {
+            $file['name']  = strip_tags($file['name']);
+            if(!isset($file['key']))   $file['key']    =   $key;
+            /* 通过扩展获取文件类型,可解决FLASH上传$FILES数组返回文件类型错误的问题 */
+            if(isset($finfo)){
+                $file['type']   =   finfo_file ( $finfo ,  $file['tmp_name'] );
+            }
+
+            /* 获取上传文件后缀,允许上传无后缀文件 */
+            $file['ext']    =   pathinfo($file['name'], PATHINFO_EXTENSION);
+
+            /* 文件上传检测 */
+            if (!$this->check($file)){
+                continue;
+            }
+
+            /* 获取文件hash */
+            if($this->hash){
+                $file['md5']  = md5_file($file['tmp_name']);
+                $file['sha1'] = sha1_file($file['tmp_name']);
+            }
+
+            /* 调用回调函数检测文件是否存在 */
+            $data = call_user_func($this->callback, $file);
+            if( $this->callback && $data ){
+                if ( file_exists('.'.$data['path'])  ) {
+                    $info[$key] = $data;
+                    continue;
+                }elseif($this->removeTrash){
+                    call_user_func($this->removeTrash,$data);//删除垃圾据
+                }
+            }
+
+            /* 生成保存文件名 */
+            $savename = $this->getSaveName($file);
+            if(false == $savename){
+                continue;
+            } else {
+                $file['savename'] = $savename;
+            }
+
+            /* 检测并创建子目录 */
+            $subpath = $this->getSubPath($file['name']);
+            if(false === $subpath){
+                continue;
+            } else {
+                $file['savepath'] = $this->savePath . $subpath;
+            }
+
+            /* 对图像文件进行严格检测 */
+            $ext = strtolower($file['ext']);
+            if(in_array($ext, array('gif','jpg','jpeg','bmp','png','swf'))) {
+                $imginfo = getimagesize($file['tmp_name']);
+                if(empty($imginfo) || ($ext == 'gif' && empty($imginfo['bits']))){
+                    $this->error = '非法图像文件!';
+                    continue;
+                }
+            }
+
+            /* 保存文件 并记录保存成功的文件 */
+            if ($this->uploader->save($file,$this->replace)) {
+                unset($file['error'], $file['tmp_name']);
+                $info[$key] = $file;
+            } else {
+                $this->error = $this->uploader->getError();
+            }
+        }
+        if(isset($finfo)){
+            finfo_close($finfo);
+        }
+        return empty($info) ? false : $info;
+    }
+
+    /**
+     * 转换上传文件数组变量为正确的方式
+     * @access private
+     * @param array $files  上传的文件变量
+     * @return array
+     */
+    private function dealFiles($files) {
+        $fileArray  = array();
+        $n          = 0;
+        foreach ($files as $key=>$file){
+            if(is_array($file['name'])) {
+                $keys       =   array_keys($file);
+                $count      =   count($file['name']);
+                for ($i=0; $i<$count; $i++) {
+                    $fileArray[$n]['key'] = $key;
+                    foreach ($keys as $_key){
+                        $fileArray[$n][$_key] = $file[$_key][$i];
+                    }
+                    $n++;
+                }
+            }else{
+               $fileArray = $files;
+               break;
+            }
+        }
+       return $fileArray;
+    }
+
+    /**
+     * 设置上传驱动
+     * @param string $driver 驱动名称
+     * @param array $config 驱动配置     
+     */
+    private function setDriver($driver = null, $config = null){
+        $driver = $driver ? : ($this->driver       ? : C('FILE_UPLOAD_TYPE'));
+        $config = $config ? : ($this->driverConfig ? : C('UPLOAD_TYPE_CONFIG'));
+        $class = strpos($driver,'\\')? $driver : 'Think\\Upload\\Driver\\'.ucfirst(strtolower($driver));
+        $this->uploader = new $class($config);
+        if(!$this->uploader){
+            E("不存在上传驱动:{$name}");
+        }
+    }
+
+    /**
+     * 检查上传的文件
+     * @param array $file 文件信息
+     */
+    private function check($file) {
+        /* 文件上传失败,捕获错误代码 */
+        if ($file['error']) {
+            $this->error($file['error']);
+            return false;
+        }
+
+        /* 无效上传 */
+        if (empty($file['name'])){
+            $this->error = '未知上传错误!';
+        }
+
+        /* 检查是否合法上传 */
+        if (!is_uploaded_file($file['tmp_name'])) {
+            $this->error = '非法上传文件!';
+            return false;
+        }
+
+        /* 检查文件大小 */
+        if (!$this->checkSize($file['size'])) {
+            $this->error = '上传文件大小不符!';
+            return false;
+        }
+
+        /* 检查文件Mime类型 */
+        //TODO:FLASH上传的文件获取到的mime类型都为application/octet-stream
+        if (!$this->checkMime($file['type'])) {
+            $this->error = '上传文件MIME类型不允许!';
+            return false;
+        }
+
+        /* 检查文件后缀 */
+        if (!$this->checkExt($file['ext'])) {
+            $this->error = '上传文件后缀不允许';
+            return false;
+        }
+
+        /* 通过检测 */
+        return true;
+    }
+
+
+    /**
+     * 获取错误代码信息
+     * @param string $errorNo  错误号
+     */
+    private function error($errorNo) {
+        switch ($errorNo) {
+            case 1:
+                $this->error = '上传的文件超过了 php.ini 中 upload_max_filesize 选项限制的值!';
+                break;
+            case 2:
+                $this->error = '上传文件的大小超过了 HTML 表单中 MAX_FILE_SIZE 选项指定的值!';
+                break;
+            case 3:
+                $this->error = '文件只有部分被上传!';
+                break;
+            case 4:
+                $this->error = '没有文件被上传!';
+                break;
+            case 6:
+                $this->error = '找不到临时文件夹!';
+                break;
+            case 7:
+                $this->error = '文件写入失败!';
+                break;
+            default:
+                $this->error = '未知上传错误!';
+        }
+    }
+
+    /**
+     * 检查文件大小是否合法
+     * @param integer $size 数据
+     */
+    private function checkSize($size) {
+        return !($size > $this->maxSize) || (0 == $this->maxSize);
+    }
+
+    /**
+     * 检查上传的文件MIME类型是否合法
+     * @param string $mime 数据
+     */
+    private function checkMime($mime) {
+        return empty($this->config['mimes']) ? true : in_array(strtolower($mime), $this->mimes);
+    }
+
+    /**
+     * 检查上传的文件后缀是否合法
+     * @param string $ext 后缀
+     */
+    private function checkExt($ext) {
+        return empty($this->config['exts']) ? true : in_array(strtolower($ext), $this->exts);
+    }
+
+    /**
+     * 根据上传文件命名规则取得保存文件名
+     * @param string $file 文件信息
+     */
+    private function getSaveName($file) {
+        $rule = $this->saveName;
+        if (empty($rule)) { //保持文件名不变
+            /* 解决pathinfo中文文件名BUG */
+            $filename = substr(pathinfo("_{$file['name']}", PATHINFO_FILENAME), 1);
+            $savename = $filename;
+        } else {
+            $savename = $this->getName($rule, $file['name']);
+            if(empty($savename)){
+                $this->error = '文件命名规则错误!';
+                return false;
+            }
+        }
+
+        /* 文件保存后缀,支持强制更改文件后缀 */
+        $ext = empty($this->config['saveExt']) ? $file['ext'] : $this->saveExt;
+
+        return $savename . '.' . $ext;
+    }
+
+    /**
+     * 获取子目录的名称
+     * @param array $file  上传的文件信息
+     */
+    private function getSubPath($filename) {
+        $subpath = '';
+        $rule    = $this->subName;
+        if ($this->autoSub && !empty($rule)) {
+            $subpath = $this->getName($rule, $filename) . '/';
+
+            if(!empty($subpath) && !$this->uploader->mkdir($this->savePath . $subpath)){
+                $this->error = $this->uploader->getError();
+                return false;
+            }
+        }
+        return $subpath;
+    }
+
+    /**
+     * 根据指定的规则获取文件或目录名称
+     * @param  array  $rule     规则
+     * @param  string $filename 原文件名
+     * @return string           文件或目录名称
+     */
+    private function getName($rule, $filename){
+        $name = '';
+        if(is_array($rule)){ //数组规则
+            $func     = $rule[0];
+            $param    = (array)$rule[1];
+            foreach ($param as &$value) {
+               $value = str_replace('__FILE__', $filename, $value);
+            }
+            $name = call_user_func_array($func, $param);
+        } elseif (is_string($rule)){ //字符串规则
+            if(function_exists($rule)){
+                $name = call_user_func($rule);
+            } else {
+                $name = $rule;
+            }
+        }
+        return $name;
+    }
+
+}

+ 238 - 0
ThinkPHP/Library/Think/Upload/Driver/Bcs.class.php

@@ -0,0 +1,238 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2014 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: Jay <yangweijiester@gmail.com> <http://code-tech.diandian.com>
+// +----------------------------------------------------------------------
+namespace Think\Upload\Driver;
+use Think\Upload\Driver\Bcs\BaiduBcs;
+class Bcs {
+    /**
+     * 上传文件根目录
+     * @var string
+     */
+    private $rootPath;
+    const DEFAULT_URL = 'bcs.duapp.com';
+
+    /**
+     * 上传错误信息
+     * @var string
+     */
+    private $error = '';
+
+    public $config = array(
+    	'AccessKey'=> '',
+        'SecretKey'=> '', //百度云服务器
+        'bucket'   => '', //空间名称
+        'rename'   => false,
+        'timeout'  => 3600, //超时时间
+    );
+
+    public $bcs = null;
+
+    /**
+     * 构造函数,用于设置上传根路径
+     * @param array  $config FTP配置
+     */
+    public function __construct($config){
+        /* 默认FTP配置 */
+        $this->config = array_merge($this->config, $config);
+        
+        $bcsClass = dirname(__FILE__). "/Bcs/bcs.class.php";
+        if(is_file($bcsClass))
+            require_once($bcsClass);
+        $this->bcs = new BaiduBCS ( $this->config['AccessKey'], $this->config['SecretKey'], self:: DEFAULT_URL );
+    }
+
+    /**
+     * 检测上传根目录(百度云上传时支持自动创建目录,直接返回)
+     * @param string $rootpath   根目录
+     * @return boolean true-检测通过,false-检测失败
+     */
+    public function checkRootPath($rootpath){
+        /* 设置根目录 */
+        $this->rootPath = str_replace('./', '/', $rootpath);
+    	return true;
+    }
+
+    /**
+     * 检测上传目录(百度云上传时支持自动创建目录,直接返回)
+     * @param  string $savepath 上传目录
+     * @return boolean          检测结果,true-通过,false-失败
+     */
+	public function checkSavePath($savepath){
+		return true;
+    }
+
+    /**
+     * 创建文件夹 (百度云上传时支持自动创建目录,直接返回)
+     * @param  string $savepath 目录名称
+     * @return boolean          true-创建成功,false-创建失败
+     */
+    public function mkdir($savepath){
+    	return true;
+    }
+
+    /**
+     * 保存指定文件
+     * @param  array   $file    保存的文件信息
+     * @param  boolean $replace 同名文件是否覆盖
+     * @return boolean          保存状态,true-成功,false-失败
+     */
+    public function save(&$file,$replace=true) {
+        $opt = array ();
+        $opt ['acl'] = BaiduBCS::BCS_SDK_ACL_TYPE_PUBLIC_WRITE;
+        $opt ['curlopts'] = array (
+            CURLOPT_CONNECTTIMEOUT => 10,
+            CURLOPT_TIMEOUT => 1800
+        );
+        $object = "/{$file['savepath']}{$file['savename']}";
+        $response = $this->bcs->create_object ( $this->config['bucket'], $object, $file['tmp_name'], $opt );
+        $url = $this->download($object);
+        $file['url'] = $url;
+        return $response->isOK() ? true : false;
+    }
+
+    public function download($file){
+        $file = str_replace('./', '/', $file);
+        $opt = array();
+        $opt['time'] = mktime('2049-12-31'); //这是最长有效时间!--
+        $response = $this->bcs->generate_get_object_url ( $this->config['bucket'], $file, $opt );
+        return $response;
+    }
+
+    /**
+     * 获取最后一次上传错误信息
+     * @return string 错误信息
+     */
+    public function getError(){
+        return $this->error;
+    }
+
+    /**
+     * 请求百度云服务器
+     * @param  string   $path    请求的PATH
+     * @param  string   $method  请求方法
+     * @param  array    $headers 请求header
+     * @param  resource $body    上传文件资源
+     * @return boolean
+     */
+    private function request($path, $method, $headers = null, $body = null){
+        $ch  = curl_init($path);
+
+        $_headers = array('Expect:');
+        if (!is_null($headers) && is_array($headers)){
+            foreach($headers as $k => $v) {
+                array_push($_headers, "{$k}: {$v}");
+            }
+        }
+
+        $length = 0;
+        $date   = gmdate('D, d M Y H:i:s \G\M\T');
+
+        if (!is_null($body)) {
+            if(is_resource($body)){
+                fseek($body, 0, SEEK_END);
+                $length = ftell($body);
+                fseek($body, 0);
+
+                array_push($_headers, "Content-Length: {$length}");
+                curl_setopt($ch, CURLOPT_INFILE, $body);
+                curl_setopt($ch, CURLOPT_INFILESIZE, $length);
+            } else {
+                $length = @strlen($body);
+                array_push($_headers, "Content-Length: {$length}");
+                curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
+            }
+        } else {
+            array_push($_headers, "Content-Length: {$length}");
+        }
+
+        // array_push($_headers, 'Authorization: ' . $this->sign($method, $uri, $date, $length));
+        array_push($_headers, "Date: {$date}");
+
+        curl_setopt($ch, CURLOPT_HTTPHEADER, $_headers);
+        curl_setopt($ch, CURLOPT_TIMEOUT, $this->config['timeout']);
+        curl_setopt($ch, CURLOPT_HEADER, 1);
+        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 0);
+        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
+
+        if ($method == 'PUT' || $method == 'POST') {
+            curl_setopt($ch, CURLOPT_POST, 1);
+        } else {
+            curl_setopt($ch, CURLOPT_POST, 0);
+        }
+
+        if ($method == 'HEAD') {
+            curl_setopt($ch, CURLOPT_NOBODY, true);
+        }
+
+        $response = curl_exec($ch);
+        $status   = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+        curl_close($ch);
+        list($header, $body) = explode("\r\n\r\n", $response, 2);
+
+        if ($status == 200) {
+            if ($method == 'GET') {
+                return $body;
+            } else {
+                $data = $this->response($header);
+                return count($data) > 0 ? $data : true;
+            }
+        } else {
+            $this->error($header);
+            return false;
+        }
+    }
+
+    /**
+     * 获取响应数据
+     * @param  string $text 响应头字符串
+     * @return array        响应数据列表
+     */
+    private function response($text){
+        $items = json_decode($text, true);
+        return $items;
+    }
+
+    /**
+     * 生成请求签名
+     * @return string          请求签名
+     */
+    private function sign($method, $Bucket, $object='/', $size=''){
+        if(!$size)
+            $size = $this->config['size'];
+        $param = array(
+            'ak'=>$this->config['AccessKey'],
+            'sk'=>$this->config['SecretKey'],
+            'size'=>$size,
+            'bucket'=>$Bucket,
+            'host'=>self :: DEFAULT_URL,
+            'date'=>time()+$this->config['timeout'],
+            'ip'=>'',
+            'object'=>$object
+        );
+        $response = $this->request($this->apiurl.'?'.http_build_query($param), 'POST');
+        if($response)
+            $response = json_decode($response, true);
+        return $response['content'][$method];
+    }
+
+
+    /**
+     * 获取请求错误信息
+     * @param  string $header 请求返回头信息
+     */
+    private function error($header) {
+        list($status, $stash) = explode("\r\n", $header, 2);
+        list($v, $code, $message) = explode(" ", $status, 3);
+        $message = is_null($message) ? 'File Not Found' : "[{$status}]:{$message}";
+        $this->error = $message;
+    }
+
+}

+ 1318 - 0
ThinkPHP/Library/Think/Upload/Driver/Bcs/bcs.class.php

@@ -0,0 +1,1318 @@
+<?php
+namespace Think\Upload\Driver\Bcs;
+use Think\Upload\Driver\Bcs\BCS_MimeTypes;
+use Think\Upload\Driver\Bcs\BCS_RequestCore;
+use Think\Upload\Driver\Bcs\BCS_ResponseCore;
+
+if (! defined ( 'BCS_API_PATH' )) {
+	define ( 'BCS_API_PATH', dirname ( __FILE__ ) );
+}
+
+//AK 公钥
+define ( 'BCS_AK', '' );
+//SK 私钥
+define ( 'BCS_SK', '' );
+//superfile 每个object分片后缀
+define ( 'BCS_SUPERFILE_POSTFIX', '_bcs_superfile_' );
+//sdk superfile分片大小 ,单位 B(字节)
+define ( 'BCS_SUPERFILE_SLICE_SIZE', 1024 * 1024 );
+
+require_once (BCS_API_PATH . '/requestcore.class.php');
+require_once (BCS_API_PATH . '/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 ();
+		require_once(dirname(__FILE__). "/requestcore.class.php");
+		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 );
+		}
+	}
+}

+ 137 - 0
ThinkPHP/Library/Think/Upload/Driver/Bcs/mimetypes.class.php

@@ -0,0 +1,137 @@
+<?php
+namespace Think\Upload\Driver\Bcs;
+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');
+	}
+}

+ 837 - 0
ThinkPHP/Library/Think/Upload/Driver/Bcs/requestcore.class.php

@@ -0,0 +1,837 @@
+<?php
+namespace Think\Upload\Driver\Bcs;
+
+/**
+ * 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) {
+				$class='\Think\Upload\Driver\Bcs\\'. $this->response_class;
+				return new  $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 ||
+                ($this->method === self::HTTP_GET &&
+                  curl_errno($curl_handle) === CURLE_PARTIAL_FILE)) {
+			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 {
+}

+ 163 - 0
ThinkPHP/Library/Think/Upload/Driver/Ftp.class.php

@@ -0,0 +1,163 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2014 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: 麦当苗儿 <zuojiazi@vip.qq.com> <http://www.zjzit.cn>
+// +----------------------------------------------------------------------
+
+namespace Think\Upload\Driver;
+class Ftp {
+    /**
+     * 上传文件根目录
+     * @var string
+     */
+    private $rootPath;
+
+    /**
+     * 本地上传错误信息
+     * @var string
+     */
+    private $error = ''; //上传错误信息
+
+    /**
+     * FTP连接
+     * @var resource
+     */
+    private $link;
+
+    private $config = array(
+        'host'     => '', //服务器
+        'port'     => 21, //端口
+        'timeout'  => 90, //超时时间
+        'username' => '', //用户名
+        'password' => '', //密码
+    );
+
+    /**
+     * 构造函数,用于设置上传根路径
+     * @param array  $config FTP配置
+     */
+    public function __construct($config){
+        /* 默认FTP配置 */
+        $this->config = array_merge($this->config, $config);
+
+        /* 登录FTP服务器 */
+        if(!$this->login()){
+            E($this->error);
+        }
+    }
+
+    /**
+     * 检测上传根目录
+     * @param string $rootpath   根目录
+     * @return boolean true-检测通过,false-检测失败
+     */
+    public function checkRootPath($rootpath){
+        /* 设置根目录 */
+        $this->rootPath = ftp_pwd($this->link) . '/' . ltrim($rootpath, '/');
+
+        if(!@ftp_chdir($this->link, $this->rootPath)){
+            $this->error = '上传根目录不存在!';
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * 检测上传目录
+     * @param  string $savepath 上传目录
+     * @return boolean          检测结果,true-通过,false-失败
+     */
+    public function checkSavePath($savepath){
+        /* 检测并创建目录 */
+        if (!$this->mkdir($savepath)) {
+            return false;
+        } else {
+            //TODO:检测目录是否可写
+            return true;
+        }
+    }
+
+    /**
+     * 保存指定文件
+     * @param  array   $file    保存的文件信息
+     * @param  boolean $replace 同名文件是否覆盖
+     * @return boolean          保存状态,true-成功,false-失败
+     */
+    public function save($file, $replace=true) {
+        $filename = $this->rootPath . $file['savepath'] . $file['savename'];
+
+        /* 不覆盖同名文件 */
+        // if (!$replace && is_file($filename)) {
+        //     $this->error = '存在同名文件' . $file['savename'];
+        //     return false;
+        // }
+
+        /* 移动文件 */
+        if (!ftp_put($this->link, $filename, $file['tmp_name'], FTP_BINARY)) {
+            $this->error = '文件上传保存错误!';
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * 创建目录
+     * @param  string $savepath 要创建的穆里
+     * @return boolean          创建状态,true-成功,false-失败
+     */
+    public function mkdir($savepath){
+        $dir = $this->rootPath . $savepath;
+        if(ftp_chdir($this->link, $dir)){
+            return true;
+        }
+
+        if(ftp_mkdir($this->link, $dir)){
+            return true;
+        } elseif($this->mkdir(dirname($savepath)) && ftp_mkdir($this->link, $dir)) {
+            return true;
+        } else {
+            $this->error = "目录 {$savepath} 创建失败!";
+            return false;
+        }
+    }
+
+    /**
+     * 获取最后一次上传错误信息
+     * @return string 错误信息
+     */
+    public function getError(){
+        return $this->error;
+    }
+
+    /**
+     * 登录到FTP服务器
+     * @return boolean true-登录成功,false-登录失败
+     */
+    private function login(){
+        extract($this->config);
+        $this->link = ftp_connect($host, $port, $timeout);
+        if($this->link) {
+            if (ftp_login($this->link, $username, $password)) {
+               return true;
+            } else {
+                $this->error = "无法登录到FTP服务器:username - {$username}";
+            }
+        } else {
+            $this->error = "无法连接到FTP服务器:{$host}";
+        }
+        return false;
+    }
+
+    /**
+     * 析构方法,用于断开当前FTP连接
+     */
+    public function __destruct() {
+        ftp_close($this->link);
+    }
+
+}

+ 118 - 0
ThinkPHP/Library/Think/Upload/Driver/Local.class.php

@@ -0,0 +1,118 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2014 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: 麦当苗儿 <zuojiazi@vip.qq.com> <http://www.zjzit.cn>
+// +----------------------------------------------------------------------
+
+namespace Think\Upload\Driver;
+class Local{
+    /**
+     * 上传文件根目录
+     * @var string
+     */
+    private $rootPath;
+
+    /**
+     * 本地上传错误信息
+     * @var string
+     */
+    private $error = ''; //上传错误信息
+
+    /**
+     * 构造函数,用于设置上传根路径
+     */
+    public function __construct($config = null){
+
+    }
+
+    /**
+     * 检测上传根目录
+     * @param string $rootpath   根目录
+     * @return boolean true-检测通过,false-检测失败
+     */
+    public function checkRootPath($rootpath){
+        if(!(is_dir($rootpath) && is_writable($rootpath))){
+            $this->error = '上传根目录不存在!请尝试手动创建:'.$rootpath;
+            return false;
+        }
+        $this->rootPath = $rootpath;
+        return true;
+    }
+
+    /**
+     * 检测上传目录
+     * @param  string $savepath 上传目录
+     * @return boolean          检测结果,true-通过,false-失败
+     */
+    public function checkSavePath($savepath){
+        /* 检测并创建目录 */
+        if (!$this->mkdir($savepath)) {
+            return false;
+        } else {
+            /* 检测目录是否可写 */
+            if (!is_writable($this->rootPath . $savepath)) {
+                $this->error = '上传目录 ' . $savepath . ' 不可写!';
+                return false;
+            } else {
+                return true;
+            }
+        }
+    }
+
+    /**
+     * 保存指定文件
+     * @param  array   $file    保存的文件信息
+     * @param  boolean $replace 同名文件是否覆盖
+     * @return boolean          保存状态,true-成功,false-失败
+     */
+    public function save($file, $replace=true) {
+        $filename = $this->rootPath . $file['savepath'] . $file['savename'];
+
+        /* 不覆盖同名文件 */ 
+        if (!$replace && is_file($filename)) {
+            $this->error = '存在同名文件' . $file['savename'];
+            return false;
+        }
+
+        /* 移动文件 */
+        if (!move_uploaded_file($file['tmp_name'], $filename)) {
+            $this->error = '文件上传保存错误!';
+            return false;
+        }
+        
+        return true;
+    }
+
+    /**
+     * 创建目录
+     * @param  string $savepath 要创建的穆里
+     * @return boolean          创建状态,true-成功,false-失败
+     */
+    public function mkdir($savepath){
+        $dir = $this->rootPath . $savepath;
+        if(is_dir($dir)){
+            return true;
+        }
+
+        if(mkdir($dir, 0777, true)){
+            return true;
+        } else {
+            $this->error = "目录 {$savepath} 创建失败!";
+            return false;
+        }
+    }
+
+    /**
+     * 获取最后一次上传错误信息
+     * @return string 错误信息
+     */
+    public function getError(){
+        return $this->error;
+    }
+
+}

+ 102 - 0
ThinkPHP/Library/Think/Upload/Driver/Qiniu.class.php

@@ -0,0 +1,102 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2014 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yangweijie <yangweijiester@gmail.com> <http://www.code-tech.diandian.com>
+// +----------------------------------------------------------------------
+
+namespace Think\Upload\Driver;
+use Think\Upload\Driver\Qiniu\QiniuStorage;
+
+class Qiniu{
+    /**
+     * 上传文件根目录
+     * @var string
+     */
+    private $rootPath;
+
+    /**
+     * 上传错误信息
+     * @var string
+     */
+    private $error = '';
+
+    private $config = array(
+        'secrectKey'     => '', //七牛服务器
+        'accessKey'      => '', //七牛用户
+        'domain'         => '', //七牛密码
+        'bucket'         => '', //空间名称
+        'timeout'        => 300, //超时时间
+    );
+
+    /**
+     * 构造函数,用于设置上传根路径
+     * @param array  $config FTP配置
+     */
+    public function __construct($config){
+        $this->config = array_merge($this->config, $config);
+        /* 设置根目录 */
+        $this->qiniu = new QiniuStorage($config);
+    }
+
+    /**
+     * 检测上传根目录(七牛上传时支持自动创建目录,直接返回)
+     * @param string $rootpath   根目录
+     * @return boolean true-检测通过,false-检测失败
+     */
+    public function checkRootPath($rootpath){
+        $this->rootPath = trim($rootpath, './') . '/';
+        return true;
+    }
+
+    /**
+     * 检测上传目录(七牛上传时支持自动创建目录,直接返回)
+     * @param  string $savepath 上传目录
+     * @return boolean          检测结果,true-通过,false-失败
+     */
+    public function checkSavePath($savepath){
+        return true;
+    }
+
+    /**
+     * 创建文件夹 (七牛上传时支持自动创建目录,直接返回)
+     * @param  string $savepath 目录名称
+     * @return boolean          true-创建成功,false-创建失败
+     */
+    public function mkdir($savepath){
+        return true;
+    }
+
+    /**
+     * 保存指定文件
+     * @param  array   $file    保存的文件信息
+     * @param  boolean $replace 同名文件是否覆盖
+     * @return boolean          保存状态,true-成功,false-失败
+     */
+    public function save(&$file,$replace=true) {
+        $file['name'] = $file['savepath'] . $file['savename'];
+        $key = str_replace('/', '_', $file['name']);
+        $upfile = array(
+            'name'=>'file',
+            'fileName'=>$key,
+            'fileBody'=>file_get_contents($file['tmp_name'])
+        );
+        $config = array();
+        $result = $this->qiniu->upload($config, $upfile);
+        $url = $this->qiniu->downlink($key);
+        $file['url'] = $url;
+        return false ===$result ? false : true;
+    }
+
+    /**
+     * 获取最后一次上传错误信息
+     * @return string 错误信息
+     */
+    public function getError(){
+        return $this->qiniu->errorStr;
+    }
+}

+ 333 - 0
ThinkPHP/Library/Think/Upload/Driver/Qiniu/QiniuStorage.class.php

@@ -0,0 +1,333 @@
+<?php
+	namespace Think\Upload\Driver\Qiniu;
+
+	class QiniuStorage {
+
+		public $QINIU_RSF_HOST = 'http://rsf.qbox.me';
+		public $QINIU_RS_HOST = 'http://rs.qbox.me';
+		public $QINIU_UP_HOST = 'http://up.qiniu.com';
+		public $timeout = '';
+
+		public function __construct($config){
+			$this->sk = $config['secrectKey'];
+			$this->ak = $config['accessKey'];
+			$this->domain = $config['domain'];
+			$this->bucket = $config['bucket'];
+			$this->timeout = isset($config['timeout'])? $config['timeout'] : 3600;
+		}
+
+		static function sign($sk, $ak, $data){
+			$sign = hash_hmac('sha1', $data, $sk, true);
+			return $ak . ':' . self::Qiniu_Encode($sign);
+		}
+
+		static function signWithData($sk, $ak, $data){
+			$data = self::Qiniu_Encode($data);
+			return self::sign($sk, $ak, $data) . ':' . $data;
+		}
+
+		public function accessToken($url, $body=''){
+			$parsed_url = parse_url($url);
+		    $path = $parsed_url['path'];
+		    $access = $path;
+		    if (isset($parsed_url['query'])) {
+		        $access .= "?" . $parsed_url['query'];
+		    }
+		    $access .= "\n";
+
+		    if($body){
+		        $access .= $body;
+		    }
+		    return self::sign($this->sk, $this->ak, $access);
+		}
+
+		public function UploadToken($sk ,$ak ,$param){
+			$param['deadline'] = $param['Expires'] == 0? 3600: $param['Expires'];
+			$param['deadline'] += time();
+			$data = array('scope'=> $this->bucket, 'deadline'=>$param['deadline']);
+			if (!empty($param['CallbackUrl'])) {
+				$data['callbackUrl'] = $param['CallbackUrl'];
+			}
+			if (!empty($param['CallbackBody'])) {
+				$data['callbackBody'] = $param['CallbackBody'];
+			}
+			if (!empty($param['ReturnUrl'])) {
+				$data['returnUrl'] = $param['ReturnUrl'];
+			}
+			if (!empty($param['ReturnBody'])) {
+				$data['returnBody'] = $param['ReturnBody'];
+			}
+			if (!empty($param['AsyncOps'])) {
+				$data['asyncOps'] = $param['AsyncOps'];
+			}
+			if (!empty($param['EndUser'])) {
+				$data['endUser'] = $param['EndUser'];
+			}
+			$data = json_encode($data);
+			return self::SignWithData($sk, $ak, $data);
+		}
+
+		public function upload($config, $file){
+			$uploadToken = $this->UploadToken($this->sk, $this->ak, $config);
+
+			$url = "{$this->QINIU_UP_HOST}";
+			$mimeBoundary = md5(microtime());
+			$header = array('Content-Type'=>'multipart/form-data;boundary='.$mimeBoundary);
+			$data = array();
+
+			$fields = array(
+				'token'=>$uploadToken,
+				'key'=>$config['saveName']? $config['save_name'] : $file['fileName'],
+			);
+
+			if(is_array($config['custom_fields']) && $config['custom_fields'] !== array()){
+				$fields = array_merge($fields, $config['custom_fields']);
+			}
+
+			foreach ($fields as $name => $val) {
+				array_push($data, '--' . $mimeBoundary);
+				array_push($data, "Content-Disposition: form-data; name=\"$name\"");
+				array_push($data, '');
+				array_push($data, $val);
+			}
+
+			//文件
+			array_push($data, '--' . $mimeBoundary);
+			$name = $file['name'];
+			$fileName = $file['fileName'];
+			$fileBody = $file['fileBody'];
+			$fileName = self::Qiniu_escapeQuotes($fileName);
+			array_push($data, "Content-Disposition: form-data; name=\"$name\"; filename=\"$fileName\"");
+			array_push($data, 'Content-Type: application/octet-stream');
+			array_push($data, '');
+			array_push($data, $fileBody);
+
+			array_push($data, '--' . $mimeBoundary . '--');
+			array_push($data, '');
+
+			$body = implode("\r\n", $data);
+			$response = $this->request($url, 'POST', $header, $body);
+			return $response;
+		}
+
+		public function dealWithType($key, $type){
+			$param = $this->buildUrlParam();
+			$url = '';
+
+			switch($type){
+				case 'img':
+					$url = $this->downLink($key);
+					if($param['imageInfo']){
+						$url .= '?imageInfo';
+					}else if($param['exif']){
+						$url .= '?exif';
+					}else if($param['imageView']){
+						$url .= '?imageView/'.$param['mode'];
+						if($param['w'])
+							$url .= "/w/{$param['w']}";
+						if($param['h'])
+							$url .= "/h/{$param['h']}";
+						if($param['q'])
+							$url .= "/q/{$param['q']}";
+						if($param['format'])
+							$url .= "/format/{$param['format']}";
+					}
+					break;
+				case 'video': //TODO 视频处理
+				case 'doc':
+					$url = $this->downLink($key);
+					$url .= '?md2html';
+					if(isset($param['mode']))
+						$url .= '/'.(int)$param['mode'];
+					if($param['cssurl'])
+						$url .= '/'. self::Qiniu_Encode($param['cssurl']);
+					break;
+
+			}
+			return $url;
+		}
+
+		public function buildUrlParam(){
+			return $_REQUEST;
+		}
+
+		//获取某个路径下的文件列表
+		public function getList($query = array(), $path = ''){
+			$query = array_merge(array('bucket'=>$this->bucket), $query);
+			$url = "{$this->QINIU_RSF_HOST}/list?".http_build_query($query);
+			$accessToken = $this->accessToken($url);
+			$response = $this->request($url, 'POST', array('Authorization'=>"QBox $accessToken"));
+			return $response;
+		}
+
+		//获取某个文件的信息
+		public function info($key){
+			$key = trim($key);
+			$url = "{$this->QINIU_RS_HOST}/stat/" . self::Qiniu_Encode("{$this->bucket}:{$key}");
+			$accessToken = $this->accessToken($url);
+			$response = $this->request($url, 'POST', array(
+				'Authorization'=>"QBox $accessToken",
+			));
+			return $response;
+		}
+
+		//获取文件下载资源链接
+		public function downLink($key){
+			$key = urlencode($key);
+			$key = self::Qiniu_escapeQuotes($key);
+			$url = "http://{$this->domain}/{$key}";
+			return $url;
+		}
+
+		//重命名单个文件
+		public function rename($file, $new_file){
+			$key = trim($file);
+			$url = "{$this->QINIU_RS_HOST}/move/" . self::Qiniu_Encode("{$this->bucket}:{$key}") .'/'. self::Qiniu_Encode("{$this->bucket}:{$new_file}");
+			trace($url);
+			$accessToken = $this->accessToken($url);
+			$response = $this->request($url, 'POST', array('Authorization'=>"QBox $accessToken"));
+			return $response;
+		}
+
+		//删除单个文件
+		public function del($file){
+			$key = trim($file);
+			$url = "{$this->QINIU_RS_HOST}/delete/" . self::Qiniu_Encode("{$this->bucket}:{$key}");
+			$accessToken = $this->accessToken($url);
+			$response = $this->request($url, 'POST', array('Authorization'=>"QBox $accessToken"));
+			return $response;
+		}
+
+		//批量删除文件
+		public function delBatch($files){
+			$url = $this->QINIU_RS_HOST . '/batch';
+			$ops = array();
+			foreach ($files as $file) {
+				$ops[] = "/delete/". self::Qiniu_Encode("{$this->bucket}:{$file}");
+			}
+			$params = 'op=' . implode('&op=', $ops);
+			$url .= '?'.$params;
+			trace($url);
+			$accessToken = $this->accessToken($url);
+			$response = $this->request($url, 'POST', array('Authorization'=>"QBox $accessToken"));
+			return $response;
+		}
+
+		static function Qiniu_Encode($str) {// URLSafeBase64Encode
+			$find = array('+', '/');
+			$replace = array('-', '_');
+			return str_replace($find, $replace, base64_encode($str));
+		}
+
+		static function Qiniu_escapeQuotes($str){
+			$find = array("\\", "\"");
+			$replace = array("\\\\", "\\\"");
+			return str_replace($find, $replace, $str);
+		}
+
+	    /**
+	     * 请求百度云服务器
+	     * @param  string   $path    请求的PATH
+	     * @param  string   $method  请求方法
+	     * @param  array    $headers 请求header
+	     * @param  resource $body    上传文件资源
+	     * @return boolean
+	     */
+	    private function request($path, $method, $headers = null, $body = null){
+	        $ch  = curl_init($path);
+
+	        $_headers = array('Expect:');
+	        if (!is_null($headers) && is_array($headers)){
+	            foreach($headers as $k => $v) {
+	                array_push($_headers, "{$k}: {$v}");
+	            }
+	        }
+
+	        $length = 0;
+			$date   = gmdate('D, d M Y H:i:s \G\M\T');
+
+	        if (!is_null($body)) {
+	            if(is_resource($body)){
+	                fseek($body, 0, SEEK_END);
+	                $length = ftell($body);
+	                fseek($body, 0);
+
+	                array_push($_headers, "Content-Length: {$length}");
+	                curl_setopt($ch, CURLOPT_INFILE, $body);
+	                curl_setopt($ch, CURLOPT_INFILESIZE, $length);
+	            } else {
+	                $length = @strlen($body);
+	                array_push($_headers, "Content-Length: {$length}");
+	                curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
+	            }
+	        } else {
+	            array_push($_headers, "Content-Length: {$length}");
+	        }
+
+	        // array_push($_headers, 'Authorization: ' . $this->sign($method, $uri, $date, $length));
+	        array_push($_headers, "Date: {$date}");
+
+	        curl_setopt($ch, CURLOPT_HTTPHEADER, $_headers);
+	        curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout);
+	        curl_setopt($ch, CURLOPT_HEADER, 1);
+	        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+	        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 0);
+			curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
+
+	        if ($method == 'PUT' || $method == 'POST') {
+				curl_setopt($ch, CURLOPT_POST, 1);
+	        } else {
+				curl_setopt($ch, CURLOPT_POST, 0);
+	        }
+
+	        if ($method == 'HEAD') {
+	            curl_setopt($ch, CURLOPT_NOBODY, true);
+	        }
+
+	        $response = curl_exec($ch);
+	        $status   = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+	        curl_close($ch);
+	        list($header, $body) = explode("\r\n\r\n", $response, 2);
+	        if ($status == 200) {
+	            if ($method == 'GET') {
+	                return $body;
+	            } else {
+	                return $this->response($response);
+	            }
+	        } else {
+	            $this->error($header , $body);
+	            return false;
+	        }
+	    }
+
+        /**
+	     * 获取响应数据
+	     * @param  string $text 响应头字符串
+	     * @return array        响应数据列表
+	     */
+	    private function response($text){
+	        $headers = explode(PHP_EOL, $text);
+	        $items = array();
+	        foreach($headers as $header) {
+	            $header = trim($header);
+	            if(strpos($header, '{') !== False){
+	                $items = json_decode($header, 1);
+	                break;
+	            }
+	        }
+	        return $items;
+	    }
+
+        /**
+	     * 获取请求错误信息
+	     * @param  string $header 请求返回头信息
+	     */
+		private function error($header, $body) {
+	        list($status, $stash) = explode("\r\n", $header, 2);
+	        list($v, $code, $message) = explode(" ", $status, 3);
+	        $message = is_null($message) ? 'File Not Found' : "[{$status}]:{$message}]";
+	        $this->error = $message;
+	        $this->errorStr = json_decode($body ,1);
+	        $this->errorStr = $this->errorStr['error'];
+	    }
+	}

+ 106 - 0
ThinkPHP/Library/Think/Upload/Driver/Sae.class.php

@@ -0,0 +1,106 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2014 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: luofei614<weibo.com/luofei614>
+// +----------------------------------------------------------------------
+
+namespace Think\Upload\Driver;
+class Sae{
+    /**
+     * Storage的Domain
+     * @var string
+     */
+    private $domain     =   '';
+
+    private $rootPath   =   '';
+
+    /**
+     * 本地上传错误信息
+     * @var string
+     */
+    private $error      =   ''; 
+
+    /**
+     * 构造函数,设置storage的domain, 如果有传配置,则domain为配置项,如果没有传domain为第一个路径的目录名称。 
+     * @param mixed $config 上传配置     
+     */
+    public function __construct($config = null){
+        if(is_array($config) && !empty($config['domain'])){
+            $this->domain   =   strtolower($config['domain']);
+        }
+    }
+
+    /**
+     * 检测上传根目录
+     * @param string $rootpath   根目录
+     * @return boolean true-检测通过,false-检测失败
+     */
+    public function checkRootPath($rootpath){
+        $rootpath = trim($rootpath,'./');
+        if(!$this->domain){
+            $rootpath = explode('/', $rootpath);
+            $this->domain = strtolower(array_shift($rootpath));
+            $rootpath = implode('/', $rootpath);
+        }
+
+        $this->rootPath =  $rootpath;
+        $st =   new \SaeStorage();
+        if(false===$st->getDomainCapacity($this->domain)){
+          $this->error  =   '您好像没有建立Storage的domain['.$this->domain.']';
+          return false;
+        }
+        return true;
+    }
+
+    /**
+     * 检测上传目录
+     * @param  string $savepath 上传目录
+     * @return boolean          检测结果,true-通过,false-失败
+     */
+    public function checkSavePath($savepath){
+        return true;
+    }
+
+    /**
+     * 保存指定文件
+     * @param  array   $file    保存的文件信息
+     * @param  boolean $replace 同名文件是否覆盖
+     * @return boolean          保存状态,true-成功,false-失败
+     */
+    public function save(&$file, $replace=true) {
+        $filename = ltrim($this->rootPath .'/'. $file['savepath'] . $file['savename'],'/');
+        $st =   new \SaeStorage();
+        /* 不覆盖同名文件 */ 
+        if (!$replace && $st->fileExists($this->domain,$filename)) {
+            $this->error = '存在同名文件' . $file['savename'];
+            return false;
+        }
+
+        /* 移动文件 */
+        if (!$st->upload($this->domain,$filename,$file['tmp_name'])) {
+            $this->error = '文件上传保存错误!['.$st->errno().']:'.$st->errmsg();
+            return false;
+        }else{
+            $file['url'] = $st->getUrl($this->domain, $filename);
+        }
+        return true;
+    }
+
+    public function mkdir(){
+        return true;
+    }
+
+    /**
+     * 获取最后一次上传错误信息
+     * @return string 错误信息
+     */
+    public function getError(){
+        return $this->error;
+    }
+
+}

+ 218 - 0
ThinkPHP/Library/Think/Upload/Driver/Upyun.class.php

@@ -0,0 +1,218 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2014 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: 麦当苗儿 <zuojiazi@vip.qq.com> <http://www.zjzit.cn>
+// +----------------------------------------------------------------------
+
+namespace Think\Upload\Driver;
+class Upyun{
+    /**
+     * 上传文件根目录
+     * @var string
+     */
+    private $rootPath;
+
+    /**
+     * 上传错误信息
+     * @var string
+     */
+    private $error = '';
+
+    private $config = array(
+        'host'     => '', //又拍云服务器
+        'username' => '', //又拍云用户
+        'password' => '', //又拍云密码
+        'bucket'   => '', //空间名称
+        'timeout'  => 90, //超时时间
+    );
+
+    /**
+     * 构造函数,用于设置上传根路径
+     * @param array  $config FTP配置
+     */
+    public function __construct($config){
+        /* 默认FTP配置 */
+        $this->config = array_merge($this->config, $config);
+        $this->config['password'] = md5($this->config['password']);
+    }
+
+    /**
+     * 检测上传根目录(又拍云上传时支持自动创建目录,直接返回)
+     * @param string $rootpath   根目录
+     * @return boolean true-检测通过,false-检测失败
+     */
+    public function checkRootPath($rootpath){
+        /* 设置根目录 */
+        $this->rootPath = trim($rootpath, './') . '/';
+        return true;
+    }
+
+    /**
+     * 检测上传目录(又拍云上传时支持自动创建目录,直接返回)
+     * @param  string $savepath 上传目录
+     * @return boolean          检测结果,true-通过,false-失败
+     */
+    public function checkSavePath($savepath){
+        return true;
+    }
+
+    /**
+     * 创建文件夹 (又拍云上传时支持自动创建目录,直接返回)
+     * @param  string $savepath 目录名称
+     * @return boolean          true-创建成功,false-创建失败
+     */
+    public function mkdir($savepath){
+        return true;
+    }
+
+    /**
+     * 保存指定文件
+     * @param  array   $file    保存的文件信息
+     * @param  boolean $replace 同名文件是否覆盖
+     * @return boolean          保存状态,true-成功,false-失败
+     */
+    public function save($file, $replace = true) {
+        $header['Content-Type'] = $file['type'];
+        $header['Content-MD5'] 	= $file['md5'];
+        $header['Mkdir'] = 'true';
+        $resource = fopen($file['tmp_name'], 'r');
+
+        $save = $this->rootPath . $file['savepath'] . $file['savename'];
+        $data = $this->request($save, 'PUT', $header, $resource);
+        return false === $data ? false : true;
+    }
+
+    /**
+     * 获取最后一次上传错误信息
+     * @return string 错误信息
+     */
+    public function getError(){
+        return $this->error;
+    }
+
+    /**
+     * 请求又拍云服务器
+     * @param  string   $path    请求的PATH
+     * @param  string   $method  请求方法
+     * @param  array    $headers 请求header
+     * @param  resource $body    上传文件资源
+     * @return boolean
+     */
+    private function request($path, $method, $headers = null, $body = null){
+        $uri = "/{$this->config['bucket']}/{$path}";
+        $ch  = curl_init($this->config['host'] . $uri);
+
+        $_headers = array('Expect:');
+        if (!is_null($headers) && is_array($headers)){
+            foreach($headers as $k => $v) {
+                array_push($_headers, "{$k}: {$v}");
+            }
+        }
+
+        $length = 0;
+        $date   = gmdate('D, d M Y H:i:s \G\M\T');
+
+        if (!is_null($body)) {
+            if(is_resource($body)){
+                fseek($body, 0, SEEK_END);
+                $length = ftell($body);
+                fseek($body, 0);
+
+                array_push($_headers, "Content-Length: {$length}");
+                curl_setopt($ch, CURLOPT_INFILE, $body);
+                curl_setopt($ch, CURLOPT_INFILESIZE, $length);
+            } else {
+                $length = @strlen($body);
+                array_push($_headers, "Content-Length: {$length}");
+                curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
+            }
+        } else {
+            array_push($_headers, "Content-Length: {$length}");
+        }
+
+        array_push($_headers, 'Authorization: ' . $this->sign($method, $uri, $date, $length));
+        array_push($_headers, "Date: {$date}");
+
+        curl_setopt($ch, CURLOPT_HTTPHEADER, $_headers);
+        curl_setopt($ch, CURLOPT_TIMEOUT, $this->config['timeout']);
+        curl_setopt($ch, CURLOPT_HEADER, 1);
+        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 0);
+        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
+
+        if ($method == 'PUT' || $method == 'POST') {
+            curl_setopt($ch, CURLOPT_POST, 1);
+        } else {
+            curl_setopt($ch, CURLOPT_POST, 0);
+        }
+
+        if ($method == 'HEAD') {
+            curl_setopt($ch, CURLOPT_NOBODY, true);
+        }
+
+        $response = curl_exec($ch);
+        $status   = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+        curl_close($ch);
+        list($header, $body) = explode("\r\n\r\n", $response, 2);
+
+        if ($status == 200) {
+            if ($method == 'GET') {
+                return $body;
+            } else {
+                $data = $this->response($header);
+                return count($data) > 0 ? $data : true;
+            }
+        } else {
+            $this->error($header);
+            return false;
+        }
+    }
+
+    /**
+     * 获取响应数据
+     * @param  string $text 响应头字符串
+     * @return array        响应数据列表
+     */
+    private function response($text){
+        $headers = explode("\r\n", $text);
+        $items = array();
+        foreach($headers as $header) {
+            $header = trim($header);
+            if(strpos($header, 'x-upyun') !== False){
+                list($k, $v) = explode(':', $header);
+                $items[trim($k)] = in_array(substr($k,8,5), array('width','heigh','frame')) ? intval($v) : trim($v);
+            }
+        }
+        return $items;
+    }
+
+    /**
+     * 生成请求签名
+     * @param  string  $method 请求方法
+     * @param  string  $uri    请求URI
+     * @param  string  $date   请求时间
+     * @param  integer $length 请求内容大小
+     * @return string          请求签名
+     */
+    private function sign($method, $uri, $date, $length){
+        $sign = "{$method}&{$uri}&{$date}&{$length}&{$this->config['password']}";
+        return 'UpYun ' . $this->config['username'] . ':' . md5($sign);
+    }
+
+    /**
+     * 获取请求错误信息
+     * @param  string $header 请求返回头信息
+     */
+    private function error($header) {
+        list($status, $stash) = explode("\r\n", $header, 2);
+        list($v, $code, $message) = explode(" ", $status, 3);
+        $message = is_null($message) ? 'File Not Found' : "[{$status}]:{$message}";
+        $this->error = $message;
+    }
+
+}