123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371 |
- <?php
- namespace sinacloud\sae;
- /**
- * 新浪云 Storage PHP 客户端
- *
- * @copyright Copyright (c) 2015, SINA, All rights reserved.
- *
- * ```php
- * <?php
- * use sinacloud\sae\Storage as Storage;
- *
- * **类初始化**
- *
- * // 方法一:在新浪云运行环境中时可以不传认证信息,默认会从应用的环境变量中取
- * $s = new Storage();
- *
- * // 方法二:如果不在新浪云运行环境或者要连非本应用的 storage,需要传入所连应用的"应用名:应用 AccessKey"和"应用 SecretKey"
- * $s = new Storage("$AppName:$AccessKey", $SecretKey);
- *
- * **Bucket 操作**
- *
- * // 创建一个 Bucket test
- * $s->putBucket("test");
- *
- * // 获取 Bucket 列表
- * $s->listBuckets();
- *
- * // 获取 Bucket 列表及 Bucket 中 Object 数量和 Bucket 的大小
- * $s->listBuckets(true);
- *
- * // 获取 test 这个 Bucket 中的 Object 对象列表,默认返回前 1000 个,如果需要返回大于 1000 个 Object 的列表,可以通过 limit 参数来指定。
- * $s->getBucket("test");
- *
- * // 获取 test 这个 Bucket 中所有以 a/ 为前缀的 Objects 列表
- * $s->getBucket("test", 'a/');
- *
- * // 获取 test 这个 Bucket 中所有以 a/ 为前缀的 Objects 列表,只显示 a/N 这个 Object 之后的列表(不包含 a/N 这个 Object)。
- * $s->getBucket("test", 'a/', 'a/N');
- *
- * // Storage 也可以当成一个伪文件系统来使用,比如获取 a/ 目录下的 Object(不显示其下的子目录的具体 Object 名称,只显示目录名)
- * $s->getBucket("test", 'a/', null, 10000, '/');
- *
- * // 删除一个空的 Bucket test
- * $s->deleteBucket("test");
- *
- * **Object 上传操作**
- *
- * // 把 $_FILES 全局变量中的缓存文件上传到 test 这个 Bucket,设置此 Object 名为 1.txt
- * $s->putObjectFile($_FILES['uploaded']['tmp_name'], "test", "1.txt");
- *
- * // 把 $_FILES 全局变量中的缓存文件上传到 test 这个 Bucket,设置此 Object 名为 sae/1.txt
- * $s->putObjectFile($_FILES['uploaded']['tmp_name'], "test", "sae/1.txt");
- *
- * // 上传一个字符串到 test 这个 Bucket 中,设置此 Object 名为 string.txt,并且设置其 Content-type
- * $s->putObject("This is string.", "test", "string.txt", array(), array('Content-Type' => 'text/plain'));
- *
- * // 上传一个文件句柄(必须是 buffer 或者一个文件,文件会被自动 fclose 掉)到 test 这个 Bucket 中,设置此 Object 名为 file.txt
- * $s->putObject(Storage::inputResource(fopen($_FILES['uploaded']['tmp_name'], 'rb'), filesize($_FILES['uploaded']['tmp_name']), "test", "file.txt", Storage::ACL_PUBLIC_READ);
- *
- * **Object 下载操作**
- *
- * // 从 test 这个 Bucket 读取 Object 1.txt,输出为此次请求的详细信息,包括状态码和 1.txt 的内容等
- * var_dump($s->getObject("test", "1.txt"));
- *
- * // 从 test 这个 Bucket 读取 Object 1.txt,把 1.txt 的内容保存在 SAE_TMP_PATH 变量指定的 TmpFS 中,savefile.txt 为保存的文件名;SAE_TMP_PATH 路径具有写权限,用户可以往这个目录下写文件,但文件的生存周期等同于 PHP 请求,也就是当该 PHP 请求完成执行时,所有写入 SAE_TMP_PATH 的文件都会被销毁
- * $s->getObject("test", "1.txt", SAE_TMP_PATH."savefile.txt");
- *
- * // 从 test 这个 Bucket 读取 Object 1.txt,把 1.txt 的内容保存在打开的文件句柄中
- * $s->getObject("test", "1.txt", fopen(SAE_TMP_PATH."savefile.txt", 'wb'));
- *
- * **Object 删除操作**
- *
- * // 从 test 这个 Bucket 删除 Object 1.txt
- * $s->deleteObject("test", "1.txt");
- *
- * **Object 复制操作**
- *
- * // 把 test 这个 Bucket 的 Object 1.txt 内容复制到 newtest 这个 Bucket 的 Object 1.txt
- * $s->copyObject("test", "1.txt", "newtest", "1.txt");
- *
- * // 把 test 这个 Bucket 的 Object 1.txt 内容复制到 newtest 这个 Bucket 的 Object 1.txt,并设置 Object 的浏览器缓存过期时间为 10s 和 Content-Type 为 text/plain
- * $s->copyObject("test", "1.txt", "newtest", "1.txt", array('expires' => '10s'), array('Content-Type' => 'text/plain'));
- *
- * **生成一个外网能够访问的 url**
- *
- * // 为私有 Bucket test 中的 Object 1.txt 生成一个能够在外网用 GET 方法临时访问的 URL,次 URL 过期时间为 600s
- * $s->getTempUrl("test", "1.txt", "GET", 600);
- *
- * // 为 test 这个 Bucket 中的 Object 1.txt 生成一个能用 CDN 访问的 URL
- * $s->getCdnUrl("test", "1.txt");
- *
- * **调试模式**
- *
- * // 开启调试模式,出问题的时候方便定位问题,设置为 true 后遇到错误的时候会抛出异常而不是写一条 warning 信息到日志。
- * $s->setExceptions(true);
- * ?>
- * ```
- */
- if (defined('SAE_APPNAME')) {
- define('DEFAULT_STORAGE_ENDPOINT', 'api.i.sinas3.com:81');
- define('DEFAULT_USE_SSL', false);
- } else {
- define('DEFAULT_STORAGE_ENDPOINT', 'api.sinas3.com');
- define('DEFAULT_USE_SSL', true);
- }
- class Storage
- {
- // ACL flags
- const ACL_PRIVATE = '';
- const ACL_PUBLIC_READ = '.r:*';
- private static $__accessKey = null;
- private static $__secretKey = null;
- private static $__account = null;
- /**
- * 默认使用的分隔符,getBucket() 等用到
- *
- * @var string
- * @access public
- * @static
- */
- public static $defDelimiter = null;
- public static $endpoint = DEFAULT_STORAGE_ENDPOINT;
- public static $proxy = null;
- /**
- * 使用 SSL 连接?
- *
- * @var bool
- * @access public
- * @static
- */
- public static $useSSL = DEFAULT_USE_SSL;
- /**
- * 是否验证 SSL 证书
- *
- * @var bool
- * @access public
- * @static
- */
- public static $useSSLValidation = false;
- /**
- * 使用的 SSL 版本
- *
- * @var const
- * @access public
- * @static
- */
- public static $useSSLVersion = 1;
- /**
- * 出现错误的时候是否使用 PHP Exception(默认使用 trigger_error 纪录错误)
- *
- * @var bool
- * @access public
- * @static
- */
- public static $useExceptions = false;
- /**
- * 构造函数
- *
- * @param string $accessKey 此处需要使用"应用名:Accesskey"
- * @param string $secretKey 应用 Secretkey
- * @param boolean $useSSL 是否使用 SSL
- * @param string $endpoint 新浪云 Storage 的 endpoint
- * @return void
- */
- public function __construct($accessKey = null, $secretKey = null,
- $useSSL = DEFAULT_USE_SSL, $endpoint = DEFAULT_STORAGE_ENDPOINT)
- {
- if ($accessKey !== null && $secretKey !== null) {
- self::setAuth($accessKey, $secretKey);
- } else if (defined('SAE_APPNAME')) {
- // We are in 新浪云运行环境
- self::setAuth(SAE_APPNAME.':'.SAE_ACCESSKEY, SAE_SECRETKEY);
- }
- self::$useSSL = $useSSL;
- self::$endpoint = $endpoint;
- }
- /**
- * 设置新浪云的 Storage 的 endpoint
- *
- * @param string $host 新浪云 Storage 的 hostname
- * @return void
- */
- public function setEndpoint($host)
- {
- self::$endpoint = $host;
- }
- /**
- * 设置访问的 Accesskey 和 Secretkey
- *
- * @param string $accessKey 此处需要使用"应用名:应用 Accesskey"
- * @param string $secretKey 应用 Secretkey
- * @return void
- */
- public static function setAuth($accessKey, $secretKey)
- {
- $e = explode(':', $accessKey);
- self::$__account = $e[0];
- self::$__accessKey = $e[1];
- self::$__secretKey = $secretKey;
- }
- public static function hasAuth() {
- return (self::$__accessKey !== null && self::$__secretKey !== null);
- }
- /**
- * 开启或者关闭 SSL
- *
- * @param boolean $enabled 是否启用 SSL
- * @param boolean $validate 是否验证 SSL 证书
- * @return void
- */
- public static function setSSL($enabled, $validate = true)
- {
- self::$useSSL = $enabled;
- self::$useSSLValidation = $validate;
- }
- /**
- * 设置代理信息
- *
- * @param string $host 代理的 hostname 和端口 (localhost:1234)
- * @param string $user 代理的 username
- * @param string $pass 代理的 password
- * @param constant $type CURL 代理类型
- * @return void
- */
- public static function setProxy($host, $user = null, $pass = null, $type = CURLPROXY_SOCKS5)
- {
- self::$proxy = array('host' => $host, 'type' => $type, 'user' => $user, 'pass' => $pass);
- }
- /**
- * 设置是否使用 PHP Exception,默认使用 trigger_error
- *
- * @param boolean $enabled Enable exceptions
- * @return void
- */
- public static function setExceptions($enabled = true)
- {
- self::$useExceptions = $enabled;
- }
- private static function __triggerError($message, $file, $line, $code = 0)
- {
- if (self::$useExceptions)
- throw new StorageException($message, $file, $line, $code);
- else
- trigger_error($message, E_USER_WARNING);
- }
- /**
- * 获取 bucket 列表
- *
- * @param boolean $detailed 设置为 true 时返回 bucket 的详细信息
- * @return array | false
- */
- public static function listBuckets($detailed = false)
- {
- $rest = new StorageRequest('GET', self::$__account, '', '', self::$endpoint);
- $rest->setParameter('format', 'json');
- $rest = $rest->getResponse();
- if ($rest->error === false && $rest->code !== 200)
- $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
- if ($rest->error !== false)
- {
- self::__triggerError(sprintf("Storage::listBuckets(): [%s] %s", $rest->error['code'],
- $rest->error['message']), __FILE__, __LINE__);
- return false;
- }
- $buckets = json_decode($rest->body, True);
- if ($buckets === False) {
- self::__triggerError(sprintf("Storage::listBuckets(): invalid body: %s", $rest->body),
- __FILE__, __LINE__);
- return false;
- }
- if ($detailed) {
- return $buckets;
- }
- $results = array();
- foreach ($buckets as $b) $results[] = (string)$b['name'];
- return $results;
- }
- /**
- * 获取 bucket 中的 object 列表
- *
- * @param string $bucket Bucket 名称
- * @param string $prefix Object 名称的前缀
- * @param string $marker Marker (返回 marker 之后的 object 列表,不包含 marker)
- * @param string $limit 最大返回的 Object 数目
- * @param string $delimiter 分隔符
- * @return array | false
- */
- public static function getBucket($bucket, $prefix = null, $marker = null, $limit = 1000, $delimiter = null, $keyword = null)
- {
- $result = array();
- do {
- $rest = new StorageRequest('GET', self::$__account, $bucket, '', self::$endpoint);
- $rest->setParameter('format', 'json');
- if ($prefix !== null && $prefix !== '') $rest->setParameter('prefix', $prefix);
- if ($marker !== null && $marker !== '') $rest->setParameter('marker', $marker);
- if ($delimiter !== null && $delimiter !== '') $rest->setParameter('delimiter', $delimiter);
- if ($keyword !== null && $keyword !== '') $rest->setParameter('keyword', $keyword);
- else if (!empty(self::$defDelimiter)) $rest->setParameter('delimiter', self::$defDelimiter);
- if ($limit > 1000) {
- $max_keys = 1000;
- } else {
- $max_keys = $limit;
- }
- $rest->setParameter("limit", $max_keys);
- $limit -= 1000;
- $response = $rest->getResponse();
- if ($response->error === false && $response->code !== 200)
- $response->error = array('code' => $response->code, 'message' => 'Unexpected HTTP status');
- if ($response->error !== false)
- {
- self::__triggerError(sprintf("Storage::getBucket(): [%s] %s", $response->error['code'],
- $response->error['message']), __FILE__, __LINE__);
- return false;
- }
- $objects = json_decode($response->body, True);
- if ($objects === False) {
- self::__triggerError(sprintf("Storage::getBucket(): invalid body: %s", $response->body),
- __FILE__, __LINE__);
- return false;
- }
- if ($objects) {
- $result = array_merge($result, $objects);
- $marker = end($objects);
- $marker = $marker['name'];
- }
- } while ($objects && count($objects) == $max_keys && $limit > 0);
- return $result;
- }
- /**
- * 创建一个 Bucket
- *
- * @param string $bucket Bucket 名称
- * @param constant $acl Bucket 的 ACL
- * @param array $metaHeaders x-sws-container-meta-* header 数组
- * @return boolean
- */
- public static function putBucket($bucket, $acl = self::ACL_PRIVATE, $metaHeaders=array())
- {
- $rest = new StorageRequest('PUT', self::$__account, $bucket, '', self::$endpoint);
- if ($acl) {
- $rest->setSwsHeader('x-sws-container-read', $acl);
- }
- foreach ($metaHeaders as $k => $v) {
- $rest->setSwsHeader('x-sws-container-meta-'.$k, $v);
- }
- $rest = $rest->getResponse();
- if ($rest->error === false && ($rest->code !== 201 && $rest->code != 202 && $rest->code !== 204))
- $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
- if ($rest->error !== false)
- {
- self::__triggerError(sprintf("Storage::putBucket({$bucket}, {$acl}): [%s] %s",
- $rest->error['code'], $rest->error['message']), __FILE__, __LINE__);
- return false;
- }
- return true;
- }
- /**
- * 获取一个 Bucket 的属性
- * @param string $bucket Bucket 名称
- * @param boolean $returnInfo 是否返回 Bucket 的信息
- * @return mixed
- */
- public static function getBucketInfo($bucket, $returnInfo=True) {
- $rest = new StorageRequest('HEAD', self::$__account, $bucket, '', self::$endpoint);
- $rest = $rest->getResponse();
- if ($rest->error === false && ($rest->code !== 204 && $rest->code !== 404))
- $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
- if ($rest->error !== false)
- {
- self::__triggerError(sprintf("Storage::getBucketInfo({$bucket}): [%s] %s",
- $rest->error['code'], $rest->error['message']), __FILE__, __LINE__);
- return false;
- }
- return $rest->code !== 404 ? $returnInfo ? $rest->headers : true : false;
- }
- /**
- * 修改一个 Bucket 的属性
- *
- * @param string $bucket Bucket 名称
- * @param constant $acl Bucket 的 ACL,null 表示不变
- * @param array $metaHeaders x-sws-container-meta-* header 数组
- * @return boolean
- */
- public static function postBucket($bucket, $acl = null, $metaHeaders=array())
- {
- $rest = new StorageRequest('POST', self::$__account, $bucket, '', self::$endpoint);
- if ($acl) {
- $rest->setSwsHeader('x-sws-container-read', $acl);
- }
- foreach ($metaHeaders as $k => $v) {
- $rest->setSwsHeader('x-sws-container-meta-'.$k, $v);
- }
- $rest = $rest->getResponse();
- if ($rest->error === false && ($rest->code !== 201 && $rest->code !== 204))
- $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
- if ($rest->error !== false)
- {
- self::__triggerError(sprintf("Storage::postBucket({$bucket}, {$acl}): [%s] %s",
- $rest->error['code'], $rest->error['message']), __FILE__, __LINE__);
- return false;
- }
- return true;
- }
- /**
- * 删除一个空的 Bucket
- *
- * @param string $bucket Bucket 名称
- * @return boolean
- */
- public static function deleteBucket($bucket)
- {
- $rest = new StorageRequest('DELETE', self::$__account, $bucket, '', self::$endpoint);
- $rest = $rest->getResponse();
- if ($rest->error === false && $rest->code !== 204)
- $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
- if ($rest->error !== false)
- {
- self::__triggerError(sprintf("Storage::deleteBucket({$bucket}): [%s] %s",
- $rest->error['code'], $rest->error['message']), __FILE__, __LINE__);
- return false;
- }
- return true;
- }
- /**
- * 为本地文件路径创建一个可以用于 putObject() 上传的 array
- *
- * @param string $file 文件路径
- * @param mixed $md5sum Use MD5 hash (supply a string if you want to use your own)
- * @return array | false
- */
- public static function inputFile($file, $md5sum = false)
- {
- if (!file_exists($file) || !is_file($file) || !is_readable($file))
- {
- self::__triggerError('Storage::inputFile(): Unable to open input file: '.$file, __FILE__, __LINE__);
- return false;
- }
- clearstatcache(false, $file);
- return array('file' => $file, 'size' => filesize($file), 'md5sum' => $md5sum !== false ?
- (is_string($md5sum) ? $md5sum : base64_encode(md5_file($file, true))) : '');
- }
- /**
- * 为打开的文件句柄创建一个可以用于 putObject() 上传的 array
- *
- * @param string $resource Input resource to read from
- * @param integer $bufferSize Input byte size
- * @param string $md5sum MD5 hash to send (optional)
- * @return array | false
- */
- public static function inputResource(&$resource, $bufferSize = false, $md5sum = '')
- {
- if (!is_resource($resource) || (int)$bufferSize < 0)
- {
- self::__triggerError('Storage::inputResource(): Invalid resource or buffer size', __FILE__, __LINE__);
- return false;
- }
- // Try to figure out the bytesize
- if ($bufferSize === false)
- {
- if (fseek($resource, 0, SEEK_END) < 0 || ($bufferSize = ftell($resource)) === false)
- {
- self::__triggerError('Storage::inputResource(): Unable to obtain resource size', __FILE__, __LINE__);
- return false;
- }
- fseek($resource, 0);
- }
- $input = array('size' => $bufferSize, 'md5sum' => $md5sum);
- $input['fp'] =& $resource;
- return $input;
- }
- /**
- * 上传一个 object
- *
- * @param mixed $input Input data
- * @param string $bucket Bucket name
- * @param string $uri Object URI
- * @param array $metaHeaders x-sws-object-meta-* header 数组
- * @param array $requestHeaders Array of request headers or content type as a string
- * @return boolean
- */
- public static function putObject($input, $bucket, $uri, $metaHeaders = array(), $requestHeaders = array())
- {
- if ($input === false) return false;
- $rest = new StorageRequest('PUT', self::$__account, $bucket, $uri, self::$endpoint);
- if (!is_array($input)) $input = array(
- 'data' => $input, 'size' => strlen($input),
- 'md5sum' => base64_encode(md5($input, true))
- );
- // Data
- if (isset($input['fp']))
- $rest->fp =& $input['fp'];
- elseif (isset($input['file']))
- $rest->fp = @fopen($input['file'], 'rb');
- elseif (isset($input['data']))
- $rest->data = $input['data'];
- // Content-Length (required)
- if (isset($input['size']) && $input['size'] >= 0)
- $rest->size = $input['size'];
- else {
- if (isset($input['file'])) {
- clearstatcache(false, $input['file']);
- $rest->size = filesize($input['file']);
- }
- elseif (isset($input['data']))
- $rest->size = strlen($input['data']);
- }
- // Custom request headers (Content-Type, Content-Disposition, Content-Encoding)
- if (is_array($requestHeaders))
- foreach ($requestHeaders as $h => $v)
- strpos($h, 'x-') === 0 ? $rest->setSwsHeader($h, $v) : $rest->setHeader($h, $v);
- if (is_string($requestHeaders)) // Support for legacy contentType parameter
- $rest->setHeader('content-type', $requestHeaders);
- else if (!isset($requestHeaders['content-type']) && !isset($requestHeaders['Content-Type']))
- $rest->setHeader('content-type', self::__getMIMEType($uri));
- // We need to post with Content-Length and Content-Type, MD5 is optional
- if ($rest->size >= 0 && ($rest->fp !== false || $rest->data !== false))
- {
- if (isset($input['md5sum'])) $rest->setHeader('Content-MD5', $input['md5sum']);
- foreach ($metaHeaders as $h => $v) $rest->setSwsHeader('x-sws-object-meta-'.$h, $v);
- $rest->getResponse();
- } else
- $rest->response->error = array('code' => 0, 'message' => 'Missing input parameters');
- if ($rest->response->error === false && $rest->response->code !== 201)
- $rest->response->error = array('code' => $rest->response->code, 'message' => 'Unexpected HTTP status');
- if ($rest->response->error !== false)
- {
- self::__triggerError(sprintf("Storage::putObject(): [%s] %s",
- $rest->response->error['code'], $rest->response->error['message']), __FILE__, __LINE__);
- return false;
- }
- return true;
- }
- /**
- * Put an object from a file (legacy function)
- *
- * @param string $file Input file path
- * @param string $bucket Bucket name
- * @param string $uri Object URI
- * @param constant $acl ACL constant
- * @param array $metaHeaders Array of x-meta-* headers
- * @param string $contentType Content type
- * @return boolean
- */
- public static function putObjectFile($file, $bucket, $uri, $metaHeaders = array(), $contentType = null)
- {
- return self::putObject(self::inputFile($file), $bucket, $uri, $metaHeaders, $contentType);
- }
- /**
- * Put an object from a string (legacy function)
- *
- * @param string $string Input data
- * @param string $bucket Bucket name
- * @param string $uri Object URI
- * @param constant $acl ACL constant
- * @param array $metaHeaders Array of x-sws-meta-* headers
- * @param string $contentType Content type
- * @return boolean
- */
- public static function putObjectString($string, $bucket, $uri, $metaHeaders = array(), $contentType = false)
- {
- return self::putObject($string, $bucket, $uri, $metaHeaders, $contentType);
- }
- /**
- * 修改一个 Object 的属性
- *
- * @param string $bucket Bucket 名称
- * @param constant $uri Object 名称
- * @param array $metaHeaders x-sws-container-meta-* header 数组
- * @param array $requestHeaders 其它 header 属性
- * @return boolean
- */
- public static function postObject($bucket, $uri, $metaHeaders=array(), $requestHeaders=Array())
- {
- $rest = new StorageRequest('POST', self::$__account, $bucket, $uri, self::$endpoint);
- foreach ($metaHeaders as $k => $v) {
- $rest->setSwsHeader('x-sws-object-meta-'.$k, $v);
- }
- foreach ($requestHeaders as $k => $v) {
- $rest->setHeader('x-sws-object-meta-'.$k, $v);
- }
- $rest = $rest->getResponse();
- if ($rest->error === false && ($rest->code !== 202))
- $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
- if ($rest->error !== false)
- {
- self::__triggerError(sprintf("Storage::postObject({$bucket}, {$uri}): [%s] %s",
- $rest->error['code'], $rest->error['message']), __FILE__, __LINE__);
- return false;
- }
- return true;
- }
- /**
- * 获取一个 Object 的内容
- *
- * @param string $bucket Bucket 名称
- * @param string $uri Object 名称
- * @param mixed $saveTo 文件保存到的文件名或者句柄
- * @return mixed 返回服务端返回的 response,其中 headers 为 Object 的属性信息,body 为 Object 的内容
- */
- public static function getObject($bucket, $uri, $saveTo = false)
- {
- $rest = new StorageRequest('GET', self::$__account, $bucket, $uri, self::$endpoint);
- if ($saveTo !== false)
- {
- if (is_resource($saveTo))
- $rest->fp =& $saveTo;
- else
- if (($rest->fp = @fopen($saveTo, 'wb')) !== false)
- $rest->file = realpath($saveTo);
- else
- $rest->response->error = array('code' => 0, 'message' => 'Unable to open save file for writing: '.$saveTo);
- }
- if ($rest->response->error === false) $rest->getResponse();
- if ($rest->response->error === false && $rest->response->code !== 200)
- $rest->response->error = array('code' => $rest->response->code, 'message' => 'Unexpected HTTP status');
- if ($rest->response->error !== false)
- {
- self::__triggerError(sprintf("Storage::getObject({$bucket}, {$uri}): [%s] %s",
- $rest->response->error['code'], $rest->response->error['message']), __FILE__, __LINE__);
- return false;
- }
- return $rest->response;
- }
- /**
- * 获取一个 Object 的信息
- *
- * @param string $bucket Bucket 名称
- * @param string $uri Object 名称
- * @param boolean $returnInfo 是否返回 Object 的详细信息
- * @return mixed | false
- */
- public static function getObjectInfo($bucket, $uri, $returnInfo = true)
- {
- $rest = new StorageRequest('HEAD', self::$__account, $bucket, $uri, self::$endpoint);
- $rest = $rest->getResponse();
- if ($rest->error === false && ($rest->code !== 200 && $rest->code !== 404))
- $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
- if ($rest->error !== false)
- {
- self::__triggerError(sprintf("Storage::getObjectInfo({$bucket}, {$uri}): [%s] %s",
- $rest->error['code'], $rest->error['message']), __FILE__, __LINE__);
- return false;
- }
- return $rest->code == 200 ? $returnInfo ? $rest->headers : true : false;
- }
- /**
- * 从一个 Bucket 复制一个 Object 到另一个 Bucket
- *
- * @param string $srcBucket 源 Bucket 名称
- * @param string $srcUri 源 Object 名称
- * @param string $bucket 目标 Bucket 名称
- * @param string $uri 目标 Object 名称
- * @param array $metaHeaders Optional array of x-sws-meta-* headers
- * @param array $requestHeaders Optional array of request headers (content type, disposition, etc.)
- * @return mixed | false
- */
- public static function copyObject($srcBucket, $srcUri, $bucket, $uri, $metaHeaders = array(), $requestHeaders = array())
- {
- $rest = new StorageRequest('PUT', self::$__account, $bucket, $uri, self::$endpoint);
- $rest->setHeader('Content-Length', 0);
- foreach ($requestHeaders as $h => $v)
- strpos($h, 'x-sws-') === 0 ? $rest->setSwsHeader($h, $v) : $rest->setHeader($h, $v);
- foreach ($metaHeaders as $h => $v) $rest->setSwsHeader('x-sws-object-meta-'.$h, $v);
- $rest->setSwsHeader('x-sws-copy-from', sprintf('/%s/%s', $srcBucket, rawurlencode($srcUri)));
- $rest->setSwsHeader('x-sws-newest', '1');
- $rest = $rest->getResponse();
- if ($rest->error === false && $rest->code !== 201)
- $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
- if ($rest->error !== false)
- {
- self::__triggerError(sprintf("Storage::copyObject({$srcBucket}, {$srcUri}, {$bucket}, {$uri}): [%s] %s",
- $rest->error['code'], $rest->error['message']), __FILE__, __LINE__);
- return false;
- }
- return isset($rest->headers['time'], $rest->headers['hash']) ? $rest->headers : false;
- }
- /**
- * Set object or bucket Access Control Policy
- *
- * @param string $bucket Bucket name
- * @param string $uri Object URI
- * @param array $acp Access Control Policy Data (same as the data returned from getAccessControlPolicy)
- * @return boolean
- */
- public static function setAccessControlPolicy($bucket, $uri = '', $acp = array())
- {
- }
- /**
- * 删除一个 Object
- *
- * @param string $bucket Bucket 名称
- * @param string $uri Object 名称
- * @return boolean
- */
- public static function deleteObject($bucket, $uri)
- {
- $rest = new StorageRequest('DELETE', self::$__account, $bucket, $uri, self::$endpoint);
- $rest = $rest->getResponse();
- if ($rest->error === false && $rest->code !== 204)
- $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
- if ($rest->error !== false)
- {
- self::__triggerError(sprintf("Storage::deleteObject(): [%s] %s",
- $rest->error['code'], $rest->error['message']), __FILE__, __LINE__);
- return false;
- }
- return true;
- }
- /**
- * 获取一个 Object 的外网直接访问 URL
- *
- * @param string $bucket Bucket 名称
- * @param string $uri Object 名称
- * @return string
- */
- public static function getUrl($bucket, $uri)
- {
- return "http://" . self::$__account . '-' . $bucket . '.stor.sinaapp.com/' . self::__encodeURI($uri);
- }
- /**
- * 获取一个 Object 的外网临时访问 URL
- *
- * @param string $bucket Bucket 名称
- * @param string $uri Object 名称
- * @param string $method Http 请求的方法,有 GET, PUT, DELETE 等
- * @param int $seconds 设置这个此 URL 的过期时间,单位是秒
- */
- public static function getTempUrl($bucket, $uri, $method, $seconds)
- {
- $expires = (int)(time() + $seconds);
- $path = "/v1/SAE_" . self::$__account . "/" . $bucket . "/" . $uri;
- $hmac_body = $method . "\n" . $expires . "\n" . $path;
- $sig = hash_hmac('sha1', $hmac_body, self::$__secretKey);
- $parameter = http_build_query(array("temp_url_sig" => $sig, "temp_url_expires" => $expires));
- return "http://" . self::$__account . '-' . $bucket . '.stor.sinaapp.com/' . self::__encodeURI($uri) . "?" . $parameter;
- }
- /**
- * 获取一个 Object 的 CDN 访问 URL
- * @param string $bucket Bucket 名称
- * @param string $uri Object 名称
- * @return string
- */
- public static function getCdnUrl($bucket, $uri)
- {
- return "http://". self::$__account . '.sae.sinacn.com/.app-stor/' . $bucket . '/' . self::__encodeURI($uri);
- }
- /**
- * 修改账户的属性(for internal use onley)
- *
- * @param array $metaHeaders x-sws-account-meta-* header 数组
- * @return boolean
- */
- public static function postAccount($metaHeaders=array())
- {
- $rest = new StorageRequest('POST', self::$__account, '', '', self::$endpoint);
- foreach ($metaHeaders as $k => $v) {
- $rest->setSwsHeader('x-sws-account-meta-'.$k, $v);
- }
- $rest = $rest->getResponse();
- if ($rest->error === false && ($rest->code !== 201 && $rest->code !== 204))
- $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
- if ($rest->error !== false)
- {
- self::__triggerError(sprintf("Storage::postAccount({$bucket}, {$acl}): [%s] %s",
- $rest->error['code'], $rest->error['message']), __FILE__, __LINE__);
- return false;
- }
- return true;
- }
- /**
- * 获取账户的属性(for internal use only)
- *
- * @param string $bucket Bucket 名称
- * @return mixed
- */
- public static function getAccountInfo() {
- $rest = new StorageRequest('HEAD', self::$__account, '', '', self::$endpoint);
- $rest = $rest->getResponse();
- if ($rest->error === false && ($rest->code !== 204 && $rest->code !== 404))
- $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
- if ($rest->error !== false)
- {
- self::__triggerError(sprintf("Storage::getAccountInfo({$bucket}): [%s] %s",
- $rest->error['code'], $rest->error['message']), __FILE__, __LINE__);
- return false;
- }
- return $rest->code !== 404 ? $rest->headers : false;
- }
- private static function __getMIMEType(&$file)
- {
- static $exts = array(
- 'jpg' => 'image/jpeg', 'jpeg' => 'image/jpeg', 'gif' => 'image/gif',
- 'png' => 'image/png', 'ico' => 'image/x-icon', 'pdf' => 'application/pdf',
- 'tif' => 'image/tiff', 'tiff' => 'image/tiff', 'svg' => 'image/svg+xml',
- 'svgz' => 'image/svg+xml', 'swf' => 'application/x-shockwave-flash',
- 'zip' => 'application/zip', 'gz' => 'application/x-gzip',
- 'tar' => 'application/x-tar', 'bz' => 'application/x-bzip',
- 'bz2' => 'application/x-bzip2', 'rar' => 'application/x-rar-compressed',
- 'exe' => 'application/x-msdownload', 'msi' => 'application/x-msdownload',
- 'cab' => 'application/vnd.ms-cab-compressed', 'txt' => 'text/plain',
- 'asc' => 'text/plain', 'htm' => 'text/html', 'html' => 'text/html',
- 'css' => 'text/css', 'js' => 'text/javascript',
- 'xml' => 'text/xml', 'xsl' => 'application/xsl+xml',
- 'ogg' => 'application/ogg', 'mp3' => 'audio/mpeg', 'wav' => 'audio/x-wav',
- 'avi' => 'video/x-msvideo', 'mpg' => 'video/mpeg', 'mpeg' => 'video/mpeg',
- 'mov' => 'video/quicktime', 'flv' => 'video/x-flv', 'php' => 'text/x-php'
- );
- $ext = strtolower(pathinfo($file, PATHINFO_EXTENSION));
- if (isset($exts[$ext])) return $exts[$ext];
- // Use fileinfo if available
- if (extension_loaded('fileinfo') && isset($_ENV['MAGIC']) &&
- ($finfo = finfo_open(FILEINFO_MIME, $_ENV['MAGIC'])) !== false)
- {
- if (($type = finfo_file($finfo, $file)) !== false)
- {
- // Remove the charset and grab the last content-type
- $type = explode(' ', str_replace('; charset=', ';charset=', $type));
- $type = array_pop($type);
- $type = explode(';', $type);
- $type = trim(array_shift($type));
- }
- finfo_close($finfo);
- if ($type !== false && strlen($type) > 0) return $type;
- }
- return 'application/octet-stream';
- }
- private static function __encodeURI($uri)
- {
- return str_replace('%2F', '/', rawurlencode($uri));
- }
- public static function __getTime()
- {
- return time() + self::$__timeOffset;
- }
- public static function __getSignature($string)
- {
- //var_dump("sign:", $string);
- return 'SWS '.self::$__accessKey.':'.self::__getHash($string);
- }
- private static function __getHash($string)
- {
- return base64_encode(extension_loaded('hash') ?
- hash_hmac('sha1', $string, self::$__secretKey, true) : pack('H*', sha1(
- (str_pad(self::$__secretKey, 64, chr(0x00)) ^ (str_repeat(chr(0x5c), 64))) .
- pack('H*', sha1((str_pad(self::$__secretKey, 64, chr(0x00)) ^
- (str_repeat(chr(0x36), 64))) . $string)))));
- }
- }
- /**
- * @ignore
- */
- final class StorageRequest
- {
- private $endpoint;
- private $verb;
- private $uri;
- private $parameters = array();
- private $swsHeaders = array();
- private $headers = array(
- 'Host' => '', 'Date' => '', 'Content-MD5' => ''
- );
- public $fp = false;
- public $size = 0;
- public $data = false;
- public $response;
- function __construct($verb, $account, $bucket = '', $uri = '', $endpoint = DEFAULT_STORAGE_ENDPOINT)
- {
- $this->endpoint = $endpoint;
- $this->verb = $verb;
- $this->uri = "/v1/SAE_" . rawurlencode($account);
- $this->resource = "/$account";
- if ($bucket !== '') {
- $this->uri = $this->uri . '/' . rawurlencode($bucket);
- $this->resource = $this->resource . '/'. $bucket;
- }
- if ($uri !== '') {
- $this->uri .= '/'.str_replace('%2F', '/', rawurlencode($uri));
- $this->resource = $this->resource . '/'. str_replace(' ', '%20', $uri);
- }
- $this->headers['Host'] = $this->endpoint;
- $this->headers['Date'] = gmdate('D, d M Y H:i:s T');
- $this->response = new \STDClass;
- $this->response->error = false;
- $this->response->body = null;
- $this->response->headers = array();
- }
- public function setParameter($key, $value)
- {
- $this->parameters[$key] = $value;
- }
- public function setHeader($key, $value)
- {
- $this->headers[$key] = $value;
- }
- public function setSwsHeader($key, $value)
- {
- $this->swsHeaders[$key] = $value;
- }
- public function getResponse()
- {
- $query = '';
- if (sizeof($this->parameters) > 0)
- {
- $query = substr($this->uri, -1) !== '?' ? '?' : '&';
- foreach ($this->parameters as $var => $value)
- if ($value == null || $value == '') $query .= $var.'&';
- else $query .= $var.'='.rawurlencode($value).'&';
- $query = substr($query, 0, -1);
- $this->uri .= $query;
- $this->resource .= $query;
- }
- $url = (Storage::$useSSL ? 'https://' : 'http://') . ($this->headers['Host'] !== '' ? $this->headers['Host'] : $this->endpoint) . $this->uri;
- //var_dump('uri: ' . $this->uri, 'url: ' . $url, 'resource: ' . $this->resource);
- // Basic setup
- $curl = curl_init();
- curl_setopt($curl, CURLOPT_USERAGENT, 'Storage/php');
- if (Storage::$useSSL)
- {
- // Set protocol version
- curl_setopt($curl, CURLOPT_SSLVERSION, Storage::$useSSLVersion);
- // SSL Validation can now be optional for those with broken OpenSSL installations
- curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, Storage::$useSSLValidation ? 2 : 0);
- curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, Storage::$useSSLValidation ? 1 : 0);
- }
- curl_setopt($curl, CURLOPT_URL, $url);
- if (Storage::$proxy != null && isset(Storage::$proxy['host']))
- {
- curl_setopt($curl, CURLOPT_PROXY, Storage::$proxy['host']);
- curl_setopt($curl, CURLOPT_PROXYTYPE, Storage::$proxy['type']);
- if (isset(Storage::$proxy['user'], Storage::$proxy['pass']) && Storage::$proxy['user'] != null && Storage::$proxy['pass'] != null)
- curl_setopt($curl, CURLOPT_PROXYUSERPWD, sprintf('%s:%s', Storage::$proxy['user'], Storage::$proxy['pass']));
- }
- // Headers
- $headers = array(); $sae = array();
- foreach ($this->swsHeaders as $header => $value)
- if (strlen($value) > 0) $headers[] = $header.': '.$value;
- foreach ($this->headers as $header => $value)
- if (strlen($value) > 0) $headers[] = $header.': '.$value;
- foreach ($this->swsHeaders as $header => $value)
- if (strlen($value) > 0) $sae[] = strtolower($header).':'.$value;
- if (sizeof($sae) > 0)
- {
- usort($sae, array(&$this, '__sortMetaHeadersCmp'));
- $sae= "\n".implode("\n", $sae);
- } else $sae = '';
- if (Storage::hasAuth())
- {
- $headers[] = 'Authorization: ' . Storage::__getSignature(
- $this->verb."\n".
- $this->headers['Date'].$sae."\n".
- $this->resource
- );
- }
- //var_dump("headers:", $headers);
- curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
- curl_setopt($curl, CURLOPT_HEADER, false);
- curl_setopt($curl, CURLOPT_RETURNTRANSFER, false);
- curl_setopt($curl, CURLOPT_WRITEFUNCTION, array(&$this, '__responseWriteCallback'));
- curl_setopt($curl, CURLOPT_HEADERFUNCTION, array(&$this, '__responseHeaderCallback'));
- // Request types
- switch ($this->verb)
- {
- case 'GET': break;
- case 'PUT': case 'POST':
- if ($this->fp !== false)
- {
- curl_setopt($curl, CURLOPT_PUT, true);
- curl_setopt($curl, CURLOPT_INFILE, $this->fp);
- if ($this->size >= 0)
- curl_setopt($curl, CURLOPT_INFILESIZE, $this->size);
- }
- elseif ($this->data !== false)
- {
- curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $this->verb);
- curl_setopt($curl, CURLOPT_POSTFIELDS, $this->data);
- }
- else
- curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $this->verb);
- break;
- case 'HEAD':
- curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'HEAD');
- curl_setopt($curl, CURLOPT_NOBODY, true);
- break;
- case 'DELETE':
- curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'DELETE');
- break;
- default: break;
- }
- // Execute, grab errors
- if (curl_exec($curl))
- $this->response->code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
- else
- $this->response->error = array(
- 'code' => curl_errno($curl),
- 'message' => curl_error($curl),
- );
- @curl_close($curl);
- // Clean up file resources
- if ($this->fp !== false && is_resource($this->fp)) fclose($this->fp);
- //var_dump("response:", $this->response);
- return $this->response;
- }
- private function __sortMetaHeadersCmp($a, $b)
- {
- $lenA = strpos($a, ':');
- $lenB = strpos($b, ':');
- $minLen = min($lenA, $lenB);
- $ncmp = strncmp($a, $b, $minLen);
- if ($lenA == $lenB) return $ncmp;
- if (0 == $ncmp) return $lenA < $lenB ? -1 : 1;
- return $ncmp;
- }
- private function __responseWriteCallback(&$curl, &$data)
- {
- if (in_array($this->response->code, array(200, 206)) && $this->fp !== false)
- return fwrite($this->fp, $data);
- else
- $this->response->body .= $data;
- return strlen($data);
- }
- private function __responseHeaderCallback($curl, $data)
- {
- if (($strlen = strlen($data)) <= 2) return $strlen;
- if (substr($data, 0, 4) == 'HTTP')
- $this->response->code = (int)substr($data, 9, 3);
- else
- {
- $data = trim($data);
- if (strpos($data, ': ') === false) return $strlen;
- list($header, $value) = explode(': ', $data, 2);
- if ($header == 'Last-Modified')
- $this->response->headers['time'] = strtotime($value);
- elseif ($header == 'Date')
- $this->response->headers['date'] = strtotime($value);
- elseif ($header == 'Content-Length')
- $this->response->headers['size'] = (int)$value;
- elseif ($header == 'Content-Type')
- $this->response->headers['type'] = $value;
- elseif ($header == 'ETag' || $header == 'Etag')
- $this->response->headers['hash'] = $value{0} == '"' ? substr($value, 1, -1) : $value;
- elseif (preg_match('/^x-sws-(?:account|container|object)-read$/i', $header))
- $this->response->headers['acl'] = $value;
- elseif (preg_match('/^x-sws-(?:account|container|object)-meta-(.*)$/i', $header))
- $this->response->headers[strtolower($header)] = $value;
- elseif (preg_match('/^x-sws-(?:account|container|object)-(.*)$/i', $header, $m))
- $this->response->headers[strtolower($m[1])] = $value;
- }
- return $strlen;
- }
- }
- /**
- * Storage 异常类
- */
- class StorageException extends \Exception {
- /**
- * 构造函数
- *
- * @param string $message 异常信息
- * @param string $file 抛出异常的文件
- * @param string $line 抛出异常的代码行
- * @param int $code 异常码
- */
- function __construct($message, $file, $line, $code = 0)
- {
- parent::__construct($message, $code);
- $this->file = $file;
- $this->line = $line;
- }
- }
- /**
- * A PHP wrapper for Storage
- *
- * @ignore
- */
- final class StorageWrapper extends Storage {
- private $position = 0, $mode = '', $buffer;
- public function url_stat($path, $flags) {
- self::__getURL($path);
- return (($info = self::getObjectInfo($this->url['host'], $this->url['path'])) !== false) ?
- array('size' => $info['size'], 'mtime' => $info['time'], 'ctime' => $info['time']) : false;
- }
- public function unlink($path) {
- self::__getURL($path);
- return self::deleteObject($this->url['host'], $this->url['path']);
- }
- public function mkdir($path, $mode, $options) {
- self::__getURL($path);
- return self::putBucket($this->url['host'], self::__translateMode($mode));
- }
- public function rmdir($path) {
- self::__getURL($path);
- return self::deleteBucket($this->url['host']);
- }
- public function dir_opendir($path, $options) {
- self::__getURL($path);
- if (($contents = self::getBucket($this->url['host'], $this->url['path'])) !== false) {
- $pathlen = strlen($this->url['path']);
- if (substr($this->url['path'], -1) == '/') $pathlen++;
- $this->buffer = array();
- foreach ($contents as $file) {
- if ($pathlen > 0) $file['name'] = substr($file['name'], $pathlen);
- $this->buffer[] = $file;
- }
- return true;
- }
- return false;
- }
- public function dir_readdir() {
- return (isset($this->buffer[$this->position])) ? $this->buffer[$this->position++]['name'] : false;
- }
- public function dir_rewinddir() {
- $this->position = 0;
- }
- public function dir_closedir() {
- $this->position = 0;
- unset($this->buffer);
- }
- public function stream_close() {
- if ($this->mode == 'w') {
- self::putObject($this->buffer, $this->url['host'], $this->url['path']);
- }
- $this->position = 0;
- unset($this->buffer);
- }
- public function stream_stat() {
- if (is_object($this->buffer) && isset($this->buffer->headers))
- return array(
- 'size' => $this->buffer->headers['size'],
- 'mtime' => $this->buffer->headers['time'],
- 'ctime' => $this->buffer->headers['time']
- );
- elseif (($info = self::getObjectInfo($this->url['host'], $this->url['path'])) !== false)
- return array('size' => $info['size'], 'mtime' => $info['time'], 'ctime' => $info['time']);
- return false;
- }
- public function stream_flush() {
- $this->position = 0;
- return true;
- }
- public function stream_open($path, $mode, $options, &$opened_path) {
- if (!in_array($mode, array('r', 'rb', 'w', 'wb'))) return false; // Mode not supported
- $this->mode = substr($mode, 0, 1);
- self::__getURL($path);
- $this->position = 0;
- if ($this->mode == 'r') {
- if (($this->buffer = self::getObject($this->url['host'], $this->url['path'])) !== false) {
- if (is_object($this->buffer->body)) $this->buffer->body = (string)$this->buffer->body;
- } else return false;
- }
- return true;
- }
- public function stream_read($count) {
- if ($this->mode !== 'r' && $this->buffer !== false) return false;
- $data = substr(is_object($this->buffer) ? $this->buffer->body : $this->buffer, $this->position, $count);
- $this->position += strlen($data);
- return $data;
- }
- public function stream_write($data) {
- if ($this->mode !== 'w') return 0;
- $left = substr($this->buffer, 0, $this->position);
- $right = substr($this->buffer, $this->position + strlen($data));
- $this->buffer = $left . $data . $right;
- $this->position += strlen($data);
- return strlen($data);
- }
- public function stream_tell() {
- return $this->position;
- }
- public function stream_eof() {
- return $this->position >= strlen(is_object($this->buffer) ? $this->buffer->body : $this->buffer);
- }
- public function stream_seek($offset, $whence) {
- switch ($whence) {
- case SEEK_SET:
- if ($offset < strlen($this->buffer->body) && $offset >= 0) {
- $this->position = $offset;
- return true;
- } else return false;
- break;
- case SEEK_CUR:
- if ($offset >= 0) {
- $this->position += $offset;
- return true;
- } else return false;
- break;
- case SEEK_END:
- $bytes = strlen($this->buffer->body);
- if ($bytes + $offset >= 0) {
- $this->position = $bytes + $offset;
- return true;
- } else return false;
- break;
- default: return false;
- }
- }
- private function __getURL($path) {
- $this->url = parse_url($path);
- if (!isset($this->url['scheme']) || $this->url['scheme'] !== 'storage') return $this->url;
- if (isset($this->url['user'], $this->url['pass'])) self::setAuth($this->url['user'], $this->url['pass']);
- $this->url['path'] = isset($this->url['path']) ? substr($this->url['path'], 1) : '';
- }
- private function __translateMode($mode) {
- $acl = self::ACL_PRIVATE;
- if (($mode & 0x0020) || ($mode & 0x0004))
- $acl = self::ACL_PUBLIC_READ;
- // You probably don't want to enable public write access
- if (($mode & 0x0010) || ($mode & 0x0008) || ($mode & 0x0002) || ($mode & 0x0001))
- $acl = self::ACL_PUBLIC_READ; //$acl = self::ACL_PUBLIC_READ_WRITE;
- return $acl;
- }
- } stream_wrapper_register('storage', 'sinacloud\sae\StorageWrapper');
|