'http', 'region' => null, 'credentials' => [ 'appId' => null, 'secretId' => '', 'secretKey' => '', 'anonymous' => false, 'token' => null, ], 'timeout' => 3600, 'connect_timeout' => 3600, 'ip' => null, 'port' => null, 'endpoint' => null, 'domain' => null, 'proxy' => null, 'retry' => 6, 'userAgent' => 'cos-php-sdk-v5.' . Client::VERSION, 'pathStyle' => false, 'signHost' => true, 'allow_redirects' => false, 'allow_accelerate' => false, 'timezone' => 'PRC', 'locationWithScheme' => false, 'autoChange' => false, 'limit_flag' => false, 'isCheckRequestPath' => true, ]; public function __construct(array $cosConfig) { $this->rawCosConfig = $cosConfig; $this->cosConfig = processCosConfig(array_replace_recursive($this->cosConfig, $cosConfig)); global $globalCosConfig; $globalCosConfig = $this->cosConfig; // check config $this->inputCheck(); $service = Service::getService(); $handler = HandlerStack::create(); $handler->push(Middleware::retry(function ($retries, $request, $response, $exception) use (&$retryCount) { $this->cosConfig['limit_flag'] = false; $retryCount = $retries; if ($retryCount >= $this->cosConfig['retry']) { return false; } if ($response) { if ($response->getStatusCode() >= 300 && !$response->hasHeader('x-cos-request-id')) { $this->cosConfig['limit_flag'] = true; return true; } if ($response->getStatusCode() >= 500 ) { return true; } } elseif ($exception) { if ($exception instanceof Exception\ServiceResponseException) { if ($exception->getStatusCode() >= 500) { $this->cosConfig['limit_flag'] = true; return true; } } if ($exception instanceof ConnectException) { return true; } } return false; }, $this->retryDelay())); $handler->push(Middleware::mapRequest(function (RequestInterface $request) use (&$retryCount) { // 获取域名 $origin_host = $request->getUri()->getHost(); // 匹配 *.cos.{Region}.myqcloud.com $pattern1 = '/\.cos\.[a-z0-9-]+\.myqcloud\.com$/'; if ($retryCount > 2 && $this->cosConfig['autoChange'] && $this->cosConfig['limit_flag'] && preg_match($pattern1, $origin_host)) { $origin = $request->getUri(); $host = str_replace("myqcloud.com", "tencentcos.cn", $origin->getHost()); // 将 URI 转换为字符串,然后替换主机名 $originUriString = (string) $origin; $originUriString = str_replace("myqcloud.com", "tencentcos.cn", $originUriString); $originUriString = str_replace($origin->getScheme() . "://", "", $originUriString); // 创建新的 URI 对象 $uri = new Uri($originUriString); // 获取路径,并从路径中移除主机名 $path = $uri->getPath(); $path = str_replace($host, '', $path); // 使用新的路径创建新的 URI $uri = $uri->withPath($path); $uri = $uri->withHost($host)->withScheme($origin->getScheme()); // 更新请求的 URI 和主机头 $request = $request->withUri($uri)->withHeader('Host', $host); return $request; } return $request; })); $handler->push(Middleware::mapRequest(function (RequestInterface $request) { return $request->withHeader('User-Agent', $this->cosConfig['userAgent']); })); if ($this->cosConfig['anonymous'] != true) { $handler->push($this::handleSignature($this->cosConfig['secretId'], $this->cosConfig['secretKey'], $this->cosConfig)); } if ($this->cosConfig['token'] != null) { $handler->push(Middleware::mapRequest(function (RequestInterface $request) { $request = $request->withHeader('x-ci-security-token', $this->cosConfig['token']); return $request->withHeader('x-cos-security-token', $this->cosConfig['token']); })); } $handler->push($this::handleErrors()); $this->signature = new Signature($this->cosConfig['secretId'], $this->cosConfig['secretKey'], $this->cosConfig, $this->cosConfig['token']); $area = $this->cosConfig['allow_accelerate'] ? 'accelerate' : $this->cosConfig['region']; $this->httpClient = new HttpClient([ 'base_uri' => "{$this->cosConfig['scheme']}://cos.{$area}.myqcloud.com/", 'timeout' => $this->cosConfig['timeout'], 'handler' => $handler, 'proxy' => $this->cosConfig['proxy'], 'allow_redirects' => $this->cosConfig['allow_redirects'] ]); $this->desc = new Description($service); $this->api = (array) $this->desc->getOperations(); parent::__construct($this->httpClient, $this->desc, [$this, 'commandToRequestTransformer'], [$this, 'responseToResultTransformer'], null); } public function inputCheck() { $message = null; //检查Region if (empty($this->cosConfig['region']) && empty($this->cosConfig['domain']) && empty($this->cosConfig['endpoint']) && empty($this->cosConfig['ip']) && !$this->cosConfig['allow_accelerate']) { $message = 'Region is empty'; } //检查Secret if (empty($this->cosConfig['secretId']) || empty($this->cosConfig['secretKey'])) { $message = 'Secret is empty'; } if ($message !== null) { $e = new Exception\CosException($message); $e->setExceptionCode('Invalid Argument'); throw $e; } } public function retryDelay() { return function ($numberOfRetries) { return 1000 * $numberOfRetries; }; } public function commandToRequestTransformer(CommandInterface $command) { $this->action = $command->GetName(); $this->operation = $this->api[$this->action]; $transformer = new CommandToRequestTransformer($this->cosConfig, $this->operation); $seri = new Serializer($this->desc); $request = $seri($command); $request = $transformer->bucketStyleTransformer($command, $request); $request = $transformer->uploadBodyTransformer($command, $request); $request = $transformer->metadataTransformer($command, $request); $request = $transformer->queryStringTransformer($command, $request); $request = $transformer->headerTransformer($command, $request); $request = $transformer->md5Transformer($command, $request); $request = $transformer->specialParamTransformer($command, $request); $request = $transformer->ciParamTransformer($command, $request); $request = $transformer->cosDomain2CiTransformer($command, $request); return $request; } public function responseToResultTransformer(ResponseInterface $response, RequestInterface $request, CommandInterface $command) { $transformer = new ResultTransformer($this->cosConfig, $this->operation); $transformer->writeDataToLocal($command, $request, $response); $deseri = new Deserializer($this->desc, true); $result = $deseri($response, $request, $command); $result = $transformer->metaDataTransformer($command, $response, $result); $result = $transformer->extraHeadersTransformer($command, $request, $result); $result = $transformer->selectContentTransformer($command, $result); $result = $transformer->ciContentInfoTransformer($command, $result); return $result; } public function __destruct() { } public function __call($method, array $args) { try { $rt = parent::__call(ucfirst($method), $args); return $rt; } catch (\Exception $e) { $previous = $e->getPrevious(); if ($previous !== null) { throw $previous; } else { throw $e; } } } public function getApi() { return $this->api; } /** * Get the config of the cos client. * * @param array|string $option * @return mixed */ public function getCosConfig($option = null) { return $option === null ? $this->cosConfig : (isset($this->cosConfig[$option]) ? $this->cosConfig[$option] : array()); } public function setCosConfig($option, $value) { $this->cosConfig[$option] = $value; } private function createPresignedUrl(RequestInterface $request, $expires) { return $this->signature->createPresignedUrl($request, $expires); } public function getPresignedUrl($method, $args, $expires = "+30 minutes") { $command = $this->getCommand($method, $args); $request = $this->commandToRequestTransformer($command); return $this->createPresignedUrl($request, $expires); } public function getObjectUrl($bucket, $key, $expires = "+30 minutes", array $args = array()) { $command = $this->getCommand('GetObject', $args + array('Bucket' => $bucket, 'Key' => $key)); $request = $this->commandToRequestTransformer($command); return $this->createPresignedUrl($request, $expires)->__toString(); } public function getObjectUrlWithoutSign($bucket, $key, array $args = array()) { $command = $this->getCommand('GetObject', $args + array('Bucket' => $bucket, 'Key' => $key)); $request = $this->commandToRequestTransformer($command); return $request->getUri()->__toString(); } public function upload($bucket, $key, $body, $options = array()) { $body = Psr7\Utils::streamFor($body); $options['Retry'] = $this->cosConfig['retry']; $options['PartSize'] = isset($options['PartSize']) ? $options['PartSize'] : MultipartUpload::DEFAULT_PART_SIZE; if ($body->getSize() < $options['PartSize']) { $rt = $this->putObject(array( 'Bucket' => $bucket, 'Key' => $key, 'Body' => $body, ) + $options); } else { $multipartUpload = new MultipartUpload($this, $body, array( 'Bucket' => $bucket, 'Key' => $key, ) + $options); $rt = $multipartUpload->performUploading(); } return $rt; } public static function simplifyPath($path) { $names = explode("/", $path); $stack = array(); foreach ($names as $name) { if ($name == "..") { if (!empty($stack)) { array_pop($stack); } } elseif ($name && $name != ".") { array_push($stack, $name); } } return "/" . implode("/", $stack); } public function download($bucket, $key, $saveAs, $options = array()) { $options['PartSize'] = isset($options['PartSize']) ? $options['PartSize'] : RangeDownload::DEFAULT_PART_SIZE; $versionId = isset($options['VersionId']) ? $options['VersionId'] : ''; if ($this->cosConfig['isCheckRequestPath'] && "/" == self::simplifyPath($key)) { $e = new Exception\CosException('Getobject Key is illegal'); $e->setExceptionCode('404'); throw $e; } $rt = $this->headObject(array( 'Bucket'=>$bucket, 'Key'=>$key, 'VersionId'=>$versionId, ) ); $contentLength = $rt['ContentLength']; $resumableJson = [ 'LastModified' => $rt['LastModified'], 'ContentLength' => $rt['ContentLength'], 'ETag' => $rt['ETag'], 'Crc64ecma' => $rt['Crc64ecma'] ]; $options['ResumableJson'] = $resumableJson; if ($contentLength < $options['PartSize']) { $rt = $this->getObject(array( 'Bucket' => $bucket, 'Key' => $key, 'SaveAs' => $saveAs, ) + $options); } else { $rangeDownload = new RangeDownload($this, $contentLength, $saveAs, array( 'Bucket' => $bucket, 'Key' => $key, ) + $options); $rt = $rangeDownload->performDownloading(); } return $rt; } public function resumeUpload($bucket, $key, $body, $uploadId, $options = array()) { $body = Psr7\Utils::streamFor($body); $options['PartSize'] = isset($options['PartSize']) ? $options['PartSize'] : MultipartUpload::DEFAULT_PART_SIZE; $multipartUpload = new MultipartUpload($this, $body, array( 'Bucket' => $bucket, 'Key' => $key, 'UploadId' => $uploadId, ) + $options); $rt = $multipartUpload->resumeUploading(); return $rt; } public function copy($bucket, $key, $copySource, $options = array()) { $options['PartSize'] = isset($options['PartSize']) ? $options['PartSize'] : Copy::DEFAULT_PART_SIZE; // set copysource client $sourceConfig = $this->rawCosConfig; $sourceConfig['region'] = $copySource['Region']; $cosSourceClient = new Client($sourceConfig); $copySource['VersionId'] = isset($copySource['VersionId']) ? $copySource['VersionId'] : ''; $rt = $cosSourceClient->headObject( array('Bucket'=>$copySource['Bucket'], 'Key'=>$copySource['Key'], 'VersionId'=>$copySource['VersionId'], ) ); $contentLength = $rt['ContentLength']; // sample copy if ($contentLength < $options['PartSize']) { $rt = $this->copyObject(array( 'Bucket' => $bucket, 'Key' => $key, 'CopySource' => "{$copySource['Bucket']}.cos.{$copySource['Region']}.myqcloud.com/". urlencode("{$copySource['Key']}")."?versionId={$copySource['VersionId']}", ) + $options ); return $rt; } // multi part copy $copySource['ContentLength'] = $contentLength; $copy = new Copy($this, $copySource, array( 'Bucket' => $bucket, 'Key' => $key ) + $options ); return $copy->copy(); } public function doesBucketExist($bucket, array $options = array()) { try { $this->HeadBucket(array( 'Bucket' => $bucket)); return true; } catch (\Exception $e){ return false; } } public function doesObjectExist($bucket, $key, array $options = array()) { try { $this->HeadObject(array( 'Bucket' => $bucket, 'Key' => $key)); return true; } catch (\Exception $e){ return false; } } public static function explodeKey($key) { global $globalCosConfig; if ($globalCosConfig['isCheckRequestPath'] && "/" == self::simplifyPath($key)) { $e = new Exception\CosException('Getobject Key is illegal'); $e->setExceptionCode('404'); throw $e; } // Remove a leading slash if one is found $split_key = explode('/', $key && $key[0] == '/' ? substr($key, 1) : $key); // Remove empty element $split_key = array_filter($split_key, function($var) { return !($var == '' || $var == null); }); $final_key = implode("/", $split_key); if (substr($key, -1) == '/') { $final_key = $final_key . '/'; } return $final_key; } public static function handleSignature($secretId, $secretKey, $options) { return function (callable $handler) use ($secretId, $secretKey, $options) { return new SignatureMiddleware($handler, $secretId, $secretKey, $options); }; } public static function handleErrors() { return function (callable $handler) { return new ExceptionMiddleware($handler); }; } }