├── .travis.yml ├── README.md ├── composer.json ├── include.php ├── phpunit.xml ├── sample.php └── src └── qcloud └── cos ├── Api.php ├── Auth.php ├── Helper.php ├── HttpClient.php ├── HttpRequest.php ├── HttpResponse.php ├── LibcurlWrapper.php ├── SliceUploading.php └── Test.php /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | php: 3 | - 5.4 4 | script: phpunit -v 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | cos-php-sdk:php sdk for [腾讯云对象存储服务](https://www.qcloud.com/product/cos.html) 2 | =================================================================================================== 3 | 4 | ## 已弃用 - 请升级到 cos-php-sdk-v5 5 | SDK 依赖的 JSON API 已弃用,请直接使用基于 XML API 的 [cos-php-sdk-v5](https://github.com/tencentyun/cos-php-sdk-v5),或者参照 [指引](https://cloud.tencent.com/document/product/436/31695) 升级到新版SDK。 6 | 7 | ### 安装(直接下载源码集成) 8 | 若出现下载失败的问题,请将代码升级到最新的版本(>=v4.3.7)。 9 | 直接从[github](https://github.com/tencentyun/cos-php-sdk-v4)下载源码,然后在您的程序中加载cos-php-sdk-v4/include.php就可以了。 10 | 11 | ### 修改配置 12 | 配置使用数组形式 13 | 14 | COS所在的区域(region),对应关系如下: 15 | 16 | |地区|region| 17 | |:--:|:--:| 18 | |华南|gz| 19 | |华中(华东)|sh| 20 | |华北|tj| 21 | 22 | ```php 23 | $config = array( 24 | 'app_id' => '', 25 | 'secret_id' => '', 26 | 'secret_key' => '', 27 | 'region' => 'gz', 28 | 'timeout' => 60 29 | ); 30 | ``` 31 | 32 | ### 示例程序 33 | 请参考sample.php 34 | 35 | ```php 36 | // 包含cos-php-sdk-v4/include.php文件 37 | require('cos-php-sdk-v4/include.php'); 38 | use QCloud\Cos\Api; 39 | 40 | $config = array( 41 | 'app_id' => '', 42 | 'secret_id' => '', 43 | 'secret_key' => '', 44 | 'region' => 'gz', 45 | 'timeout' => 60 46 | ); 47 | $cosApi = new Api($config); 48 | 49 | // 创建文件夹 50 | $ret = $cosApi->createFolder($bucket, $folder); 51 | var_dump($ret); 52 | 53 | // 上传文件 54 | $ret = $cosApi->upload($bucket, $src, $dst); 55 | var_dump($ret); 56 | 57 | // 下载文件 58 | $ret = $cosApi->download($bucket, $src, $dst); 59 | var_dump($ret); 60 | 61 | // 目录列表 62 | $ret = $cosApi->listFolder($bucket, $folder); 63 | var_dump($ret); 64 | 65 | // 更新目录信息 66 | $bizAttr = ""; 67 | $ret = $cosApi->updateFolder($bucket, $folder, $bizAttr); 68 | var_dump($ret); 69 | 70 | // 更新文件信息 71 | $bizAttr = ''; 72 | $authority = 'eWPrivateRPublic'; 73 | $customerHeaders = array( 74 | 'Cache-Control' => 'no', 75 | 'Content-Type' => 'application/pdf', 76 | 'Content-Language' => 'ch', 77 | ); 78 | $ret = $cosApi->update($bucket, $dst, $bizAttr, $authority, $customerHeaders); 79 | var_dump($ret); 80 | 81 | // 查询目录信息 82 | $ret = $cosApi->statFolder($bucket, $folder); 83 | var_dump($ret); 84 | 85 | // 查询文件信息 86 | $ret = $cosApi->stat($bucket, $dst); 87 | var_dump($ret); 88 | 89 | // 删除文件 90 | $ret = $cosApi->delFile($bucket, $dst); 91 | var_dump($ret); 92 | 93 | // 删除目录 94 | $ret = $cosApi->delFolder($bucket, $folder); 95 | var_dump($ret); 96 | 97 | // 复制文件 98 | $ret = $cosApi->copyFile($bucket, '/111.txt', '/111_2.txt'); 99 | var_dump($ret); 100 | 101 | // 移动文件 102 | $ret = $cosApi->moveFile($bucket, '/111.txt', '/111_3.txt'); 103 | var_dump($ret); 104 | ``` 105 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "qcloud/cos-sdk-v4", 3 | "description": "PHP SDK for QCloud COS", 4 | "keywords": [ 5 | "qcloud", "cos", "php" 6 | ], 7 | "license": "MIT", 8 | "authors": [ 9 | { 10 | "name": "yaozongyou", 11 | "email": "yaozongyou@vip.qq.com" 12 | }, 13 | { 14 | "name": "ACTom", 15 | "email": "i@actom.me" 16 | } 17 | ], 18 | "autoload": { 19 | "psr-4": { 20 | "QCloud\\Cos\\": "src/qcloud/cos/" 21 | } 22 | }, 23 | "require": { 24 | "php": ">=5.3.0" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /include.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | src/qcloud/cos 4 | 5 | 6 | -------------------------------------------------------------------------------- /sample.php: -------------------------------------------------------------------------------- 1 | '', 20 | 'secret_id' => '', 21 | 'secret_key' => '', 22 | 'region' => '', // bucket所属地域:华北 'tj' 华东 'sh' 华南 'gz' 23 | 'timeout' => 60 24 | ); 25 | 26 | date_default_timezone_set('PRC'); 27 | $cosApi = new Api($config); 28 | 29 | // Create folder in bucket. 30 | $ret = $cosApi->createFolder($bucket, $folder); 31 | var_dump($ret); 32 | 33 | // Upload file into bucket. 34 | $ret = $cosApi->upload($bucket, $src, $dst); 35 | var_dump($ret); 36 | 37 | // Download file 38 | $ret = $cosApi->download($bucket, $dst, $dst2); 39 | var_dump($ret); 40 | unlink($dst2); 41 | 42 | // List folder. 43 | $ret = $cosApi->listFolder($bucket, $folder); 44 | var_dump($ret); 45 | 46 | // Update folder information. 47 | $bizAttr = ""; 48 | $ret = $cosApi->updateFolder($bucket, $folder, $bizAttr); 49 | var_dump($ret); 50 | 51 | // Update file information. 52 | $bizAttr = ''; 53 | $authority = 'eWPrivateRPublic'; 54 | $customerHeaders = array( 55 | 'Cache-Control' => 'no', 56 | 'Content-Type' => 'application/pdf', 57 | 'Content-Language' => 'ch', 58 | ); 59 | $ret = $cosApi->update($bucket, $dst, $bizAttr,$authority, $customerHeaders); 60 | var_dump($ret); 61 | 62 | // Stat folder. 63 | $ret = $cosApi->statFolder($bucket, $folder); 64 | var_dump($ret); 65 | 66 | // Stat file. 67 | $ret = $cosApi->stat($bucket, $dst); 68 | var_dump($ret); 69 | 70 | // Copy file. 71 | $ret = $cosApi->copyFile($bucket, $dst, $dst . '_copy'); 72 | var_dump($ret); 73 | 74 | // Move file. 75 | $ret = $cosApi->moveFile($bucket, $dst, $dst . '_move'); 76 | var_dump($ret); 77 | 78 | // Delete file. 79 | $ret = $cosApi->delFile($bucket, $dst . '_copy'); 80 | var_dump($ret); 81 | $ret = $cosApi->delFile($bucket, $dst . '_move'); 82 | var_dump($ret); 83 | 84 | // Delete folder. 85 | $ret = $cosApi->delFolder($bucket, $folder); 86 | var_dump($ret); 87 | -------------------------------------------------------------------------------- /src/qcloud/cos/Api.php: -------------------------------------------------------------------------------- 1 | config = $config; 35 | $this->auth = new Auth($config['app_id'], $config['secret_id'], $config['secret_key']); 36 | $this->httpClient = new HttpClient(); 37 | 38 | if (isset($config['region'])) { 39 | $this->setRegion($config['region']); 40 | } 41 | 42 | if (isset($config['timeout'])) { 43 | $this->setTimeout($config['timeout']); 44 | } 45 | } 46 | 47 | /** 48 | * 设置HTTP请求超时时间 49 | * @param int $timeout 超时时长 50 | */ 51 | public function setTimeout($timeout = 60) { 52 | if (!is_int($timeout) || $timeout < 0) { 53 | return false; 54 | } 55 | 56 | $this->timeout = $timeout; 57 | return true; 58 | } 59 | 60 | public function setRegion($region) { 61 | $this->region = $region; 62 | } 63 | 64 | /** 65 | * 上传文件,自动判断文件大小,如果小于20M则使用普通文件上传,大于20M则使用分片上传 66 | * @param string $bucket bucket名称 67 | * @param string $srcPath 本地文件路径 68 | * @param string $dstPath 上传的文件路径 69 | * @param string $bizAttr 文件属性 70 | * @param string $slicesize 分片大小(512k,1m,2m,3m),默认:1m 71 | * @param string $insertOnly 同名文件是否覆盖 72 | * @return [type] [description] 73 | */ 74 | public function upload( 75 | $bucket, $srcPath, $dstPath, $bizAttr=null, $sliceSize=null, $insertOnly=null) { 76 | if (!file_exists($srcPath)) { 77 | return array( 78 | 'code' => self::COSAPI_PARAMS_ERROR, 79 | 'message' => 'file ' . $srcPath .' not exists', 80 | 'data' => array() 81 | ); 82 | } 83 | 84 | if (!$dstPath || !is_string($dstPath) 85 | || $dstPath[strlen($dstPath) - 1] == '/') { 86 | return array( 87 | 'code' => self::COSAPI_PARAMS_ERROR, 88 | 'message' => 'dstPath ' . $dstPath .' invalid', 89 | 'data' => array() 90 | ); 91 | } 92 | 93 | $dstPath = $this->normalizerPath($dstPath, false); 94 | 95 | //文件大于20M则使用分片传输 96 | if (filesize($srcPath) < self::MAX_UNSLICE_FILE_SIZE ) { 97 | return $this->uploadFile($bucket, $srcPath, $dstPath, $bizAttr, $insertOnly); 98 | } else { 99 | $sliceSize = $this->getSliceSize($sliceSize); 100 | return $this->uploadBySlicing($bucket, $srcPath, $dstPath, $bizAttr, $sliceSize, $insertOnly); 101 | } 102 | } 103 | 104 | /* * 105 | * 上传内存中的内容 106 | * @param string $bucket bucket名称 107 | * @param string $content 文件内容,二进制安全 108 | * @param string $dstPath 上传的文件路径 109 | * @param string $bizAttr 文件属性 110 | * @param int $insertOnly 是否覆盖同名文件:0 覆盖,1:不覆盖 111 | * 112 | * */ 113 | public function uploadBuffer( 114 | $bucket, $content, $dstPath, 115 | $bizAttr=null, $insertOnly=null) { 116 | 117 | if (strlen($content) >= self::MAX_UNSLICE_FILE_SIZE) { 118 | return array( 119 | 'code' => self::COSAPI_PARAMS_ERROR, 120 | 'message' => 'content larger then 20M, not supported', 121 | 'data' => array() 122 | ); 123 | } 124 | 125 | if (!$dstPath || !is_string($dstPath) 126 | || $dstPath[strlen($dstPath) - 1] == '/') { 127 | return array( 128 | 'code' => self::COSAPI_PARAMS_ERROR, 129 | 'message' => 'dstPath ' . $dstPath .' invalid', 130 | 'data' => array() 131 | ); 132 | } 133 | 134 | $dstPath = $this->cosUrlEncode($dstPath); 135 | $expired = time() + self::EXPIRED_SECONDS; 136 | $url = $this->generateResUrl($bucket, $dstPath); 137 | $signature = $this->auth->createReusableSignature($expired, $bucket); 138 | $fileSha = sha1($content); 139 | 140 | $data = array( 141 | 'op' => 'upload', 142 | 'sha' => $fileSha, 143 | 'biz_attr' => (isset($bizAttr) ? $bizAttr : ''), 144 | 'filecontent' => $content, 145 | ); 146 | 147 | if (isset($insertOnly) && strlen($insertOnly) > 0) { 148 | $data['insertOnly'] = (($insertOnly == 0 || $insertOnly == '0' ) ? 0 : 1); 149 | } 150 | 151 | $req = array( 152 | 'url' => $url, 153 | 'method' => 'post', 154 | 'timeout' => $this->timeout, 155 | 'data' => $data, 156 | 'header' => array( 157 | 'Authorization: ' . $signature, 158 | ), 159 | ); 160 | 161 | return $this->sendRequest($req); 162 | } 163 | 164 | /** 165 | * 下载文件 166 | * @param string $bucket bucket名称 167 | * @param string $srcPath 本地文件路径 168 | * @param string $dstPath 上传的文件路径 169 | * @return [type] [description] 170 | */ 171 | public function download($bucket, $srcPath, $dstPath) { 172 | $srcInfo = $this->stat($bucket, $srcPath); 173 | if ($srcInfo['code'] !== 0) { 174 | return array( 175 | 'code' => self::COSAPI_PARAMS_ERROR, 176 | 'message' => 'file '.$srcPath.' does not exists.', 177 | 'data' => array() 178 | ); 179 | } 180 | 181 | $url = $srcInfo['data']['source_url']; 182 | $sha = $srcInfo['data']['sha']; 183 | $expired = time() + self::EXPIRED_SECONDS; 184 | $signature = $this->auth->createReusableSignature($expired, $bucket); 185 | $req = array( 186 | 'url' => $url, 187 | 'method' => 'get', 188 | 'timeout' => $this->timeout, 189 | 'header' => array( 190 | 'Authorization: ' . $signature, 191 | ), 192 | ); 193 | 194 | $result = $this->httpClient->download($req, $dstPath); 195 | if ($result['code'] !== self::COSAPI_SUCCESS) { 196 | return array( 197 | 'code' => $result['code'], 198 | 'message' => $result['message'], 199 | 'data' => array() 200 | ); 201 | } 202 | return array( 203 | 'code' => self::COSAPI_SUCCESS, 204 | 'message' => 'SUCCESS', 205 | 'data' => array() 206 | ); 207 | } 208 | 209 | /* 210 | * 创建目录 211 | * @param string $bucket bucket名称 212 | * @param string $folder 目录路径 213 | * @param string $bizAttr 目录属性 214 | */ 215 | public function createFolder($bucket, $folder, $bizAttr = null) { 216 | if (!$this->isValidPath($folder)) { 217 | return array( 218 | 'code' => self::COSAPI_PARAMS_ERROR, 219 | 'message' => 'folder ' . $folder . ' is not a valid folder name', 220 | 'data' => array() 221 | ); 222 | } 223 | 224 | $folder = $this->normalizerPath($folder, True); 225 | $folder = $this->cosUrlEncode($folder); 226 | $expired = time() + self::EXPIRED_SECONDS; 227 | $url = $this->generateResUrl($bucket, $folder); 228 | $signature = $this->auth->createReusableSignature($expired, $bucket); 229 | 230 | $data = array( 231 | 'op' => 'create', 232 | 'biz_attr' => (isset($bizAttr) ? $bizAttr : ''), 233 | ); 234 | 235 | $data = json_encode($data); 236 | 237 | $req = array( 238 | 'url' => $url, 239 | 'method' => 'post', 240 | 'timeout' => $this->timeout, 241 | 'data' => $data, 242 | 'header' => array( 243 | 'Authorization: ' . $signature, 244 | 'Content-Type: application/json', 245 | ), 246 | ); 247 | 248 | return $this->sendRequest($req); 249 | } 250 | 251 | /* 252 | * 目录列表 253 | * @param string $bucket bucket名称 254 | * @param string $path 目录路径,sdk会补齐末尾的 '/' 255 | * @param int $num 拉取的总数 256 | * @param string $offset 透传字段,用于翻页,前端不需理解,需要往前/往后翻页则透传回来 257 | */ 258 | public function listFolder( 259 | $bucket, $folder, $num = 20, 260 | $context = null) { 261 | $folder = $this->normalizerPath($folder, True); 262 | 263 | return $this->listBase($bucket, $folder, $num, $context); 264 | } 265 | 266 | /* 267 | * 目录列表(前缀搜索) 268 | * @param string $bucket bucket名称 269 | * @param string $prefix 列出含此前缀的所有文件 270 | * @param int $num 拉取的总数 271 | * @param string $offset 透传字段,用于翻页,前端不需理解,需要往前/往后翻页则透传回来 272 | */ 273 | public function prefixSearch( 274 | $bucket, $prefix, $num = 20, 275 | $context = null) { 276 | $path = $this->normalizerPath($prefix); 277 | 278 | return $this->listBase($bucket, $prefix, $num, $context); 279 | } 280 | 281 | /* 282 | * 目录更新 283 | * @param string $bucket bucket名称 284 | * @param string $folder 文件夹路径,SDK会补齐末尾的 '/' 285 | * @param string $bizAttr 目录属性 286 | */ 287 | public function updateFolder($bucket, $folder, $bizAttr = null) { 288 | $folder = $this->normalizerPath($folder, True); 289 | 290 | return $this->updateBase($bucket, $folder, $bizAttr); 291 | } 292 | 293 | /* 294 | * 查询目录信息 295 | * @param string $bucket bucket名称 296 | * @param string $folder 目录路径 297 | */ 298 | public function statFolder($bucket, $folder) { 299 | $folder = $this->normalizerPath($folder, True); 300 | 301 | return $this->statBase($bucket, $folder); 302 | } 303 | 304 | /* 305 | * 删除目录 306 | * @param string $bucket bucket名称 307 | * @param string $folder 目录路径 308 | * 注意不能删除bucket下根目录/ 309 | */ 310 | public function delFolder($bucket, $folder) { 311 | if (empty($bucket) || empty($folder)) { 312 | return array( 313 | 'code' => self::COSAPI_PARAMS_ERROR, 314 | 'message' => 'bucket or path is empty'); 315 | } 316 | 317 | $folder = $this->normalizerPath($folder, True); 318 | 319 | return $this->delBase($bucket, $folder); 320 | } 321 | 322 | /* 323 | * 更新文件 324 | * @param string $bucket bucket名称 325 | * @param string $path 文件路径 326 | * @param string $authority: eInvalid(继承Bucket的读写权限)/eWRPrivate(私有读写)/eWPrivateRPublic(公有读私有写) 327 | * @param array $customer_headers_array 携带的用户自定义头域,包括 328 | * 'Cache-Control' => '*' 329 | * 'Content-Type' => '*' 330 | * 'Content-Disposition' => '*' 331 | * 'Content-Language' => '*' 332 | * 'x-cos-meta-自定义内容' => '*' 333 | */ 334 | public function update($bucket, $path, 335 | $bizAttr = null, $authority=null,$customer_headers_array=null) { 336 | $path = $this->normalizerPath($path); 337 | 338 | return $this->updateBase($bucket, $path, $bizAttr, $authority, $customer_headers_array); 339 | } 340 | 341 | /* 342 | * 查询文件信息 343 | * @param string $bucket bucket名称 344 | * @param string $path 文件路径 345 | */ 346 | public function stat($bucket, $path) { 347 | $path = $this->normalizerPath($path); 348 | 349 | return $this->statBase($bucket, $path); 350 | } 351 | 352 | /* 353 | * 删除文件 354 | * @param string $bucket 355 | * @param string $path 文件路径 356 | */ 357 | public function delFile($bucket, $path) { 358 | if (empty($bucket) || empty($path)) { 359 | return array( 360 | 'code' => self::COSAPI_PARAMS_ERROR, 361 | 'message' => 'path is empty'); 362 | } 363 | 364 | $path = $this->normalizerPath($path); 365 | 366 | return $this->delBase($bucket, $path); 367 | } 368 | 369 | /** 370 | * 内部方法, 上传文件 371 | * @param string $bucket bucket名称 372 | * @param string $srcPath 本地文件路径 373 | * @param string $dstPath 上传的文件路径 374 | * @param string $bizAttr 文件属性 375 | * @param int $insertOnly 是否覆盖同名文件:0 覆盖,1:不覆盖 376 | * @return [type] [description] 377 | */ 378 | private function uploadFile($bucket, $srcPath, $dstPath, $bizAttr = null, $insertOnly = null) { 379 | $srcPath = realpath($srcPath); 380 | $dstPath = $this->cosUrlEncode($dstPath); 381 | 382 | if (filesize($srcPath) >= self::MAX_UNSLICE_FILE_SIZE ) { 383 | return array( 384 | 'code' => self::COSAPI_PARAMS_ERROR, 385 | 'message' => 'file '.$srcPath.' larger then 20M, please use uploadBySlicing interface', 386 | 'data' => array() 387 | ); 388 | } 389 | 390 | $expired = time() + self::EXPIRED_SECONDS; 391 | $url = $this->generateResUrl($bucket, $dstPath); 392 | $signature = $this->auth->createReusableSignature($expired, $bucket); 393 | $fileSha = hash_file('sha1', $srcPath); 394 | 395 | $data = array( 396 | 'op' => 'upload', 397 | 'sha' => $fileSha, 398 | 'biz_attr' => (isset($bizAttr) ? $bizAttr : ''), 399 | ); 400 | 401 | $data['filecontent'] = file_get_contents($srcPath); 402 | 403 | if (isset($insertOnly) && strlen($insertOnly) > 0) { 404 | $data['insertOnly'] = (($insertOnly == 0 || $insertOnly == '0' ) ? 0 : 1); 405 | } 406 | 407 | $req = array( 408 | 'url' => $url, 409 | 'method' => 'post', 410 | 'timeout' => $this->timeout, 411 | 'data' => $data, 412 | 'header' => array( 413 | 'Authorization: ' . $signature, 414 | ), 415 | ); 416 | 417 | return $this->sendRequest($req); 418 | } 419 | 420 | /** 421 | * 内部方法,上传文件 422 | * @param string $bucket bucket名称 423 | * @param string $srcPath 本地文件路径 424 | * @param string $dstPath 上传的文件路径 425 | * @param string $bizAttr 文件属性 426 | * @param string $sliceSize 分片大小 427 | * @param int $insertOnly 是否覆盖同名文件:0 覆盖,1:不覆盖 428 | * @return [type] [description] 429 | */ 430 | private function uploadBySlicing( 431 | $bucket, $srcFpath, $dstFpath, $bizAttr=null, $sliceSize=null, $insertOnly=null) { 432 | $srcFpath = realpath($srcFpath); 433 | $fileSize = filesize($srcFpath); 434 | $dstFpath = $this->cosUrlEncode($dstFpath); 435 | $url = $this->generateResUrl($bucket, $dstFpath); 436 | $sliceCount = ceil($fileSize / $sliceSize); 437 | // expiration seconds for one slice mutiply by slice count 438 | // will be the expired seconds for whole file 439 | $expiration = time() + (self::EXPIRED_SECONDS * $sliceCount); 440 | if ($expiration >= (time() + 10 * 24 * 60 * 60)) { 441 | $expiration = time() + 10 * 24 * 60 * 60; 442 | } 443 | $signature = $this->auth->createReusableSignature($expiration, $bucket); 444 | 445 | $sliceUploading = new SliceUploading($this->timeout * 1000, self::MAX_RETRY_TIMES); 446 | for ($tryCount = 0; $tryCount < self::MAX_RETRY_TIMES; ++$tryCount) { 447 | if ($sliceUploading->initUploading( 448 | $signature, 449 | $srcFpath, 450 | $url, 451 | $fileSize, $sliceSize, $bizAttr, $insertOnly)) { 452 | break; 453 | } 454 | 455 | $errorCode = $sliceUploading->getLastErrorCode(); 456 | if ($errorCode === -4019) { 457 | // Delete broken file and retry again on _ERROR_FILE_NOT_FINISH_UPLOAD error. 458 | Cosapi::delFile($bucket, $dstFpath); 459 | continue; 460 | } 461 | 462 | if ($tryCount === self::MAX_RETRY_TIMES - 1) { 463 | return array( 464 | 'code' => $sliceUploading->getLastErrorCode(), 465 | 'message' => $sliceUploading->getLastErrorMessage(), 466 | 'request_id' => $sliceUploading->getRequestId(), 467 | ); 468 | } 469 | } 470 | 471 | if (!$sliceUploading->performUploading()) { 472 | return array( 473 | 'code' => $sliceUploading->getLastErrorCode(), 474 | 'message' => $sliceUploading->getLastErrorMessage(), 475 | 'request_id' => $sliceUploading->getRequestId(), 476 | ); 477 | } 478 | 479 | if (!$sliceUploading->finishUploading()) { 480 | return array( 481 | 'code' => $sliceUploading->getLastErrorCode(), 482 | 'message' => $sliceUploading->getLastErrorMessage(), 483 | 'request_id' => $sliceUploading->getRequestId(), 484 | ); 485 | } 486 | 487 | return array( 488 | 'code' => 0, 489 | 'message' => 'SUCCESS', 490 | 'request_id' => $sliceUploading->getRequestId(), 491 | 'data' => array( 492 | 'access_url' => $sliceUploading->getAccessUrl(), 493 | 'resource_path' => $sliceUploading->getResourcePath(), 494 | 'source_url' => $sliceUploading->getSourceUrl(), 495 | ), 496 | ); 497 | } 498 | 499 | /* 500 | * 内部公共函数 501 | * @param string $bucket bucket名称 502 | * @param string $path 文件夹路径 503 | * @param int $num 拉取的总数 504 | * @param string $context 在翻页查询时候用到 505 | */ 506 | private function listBase( 507 | $bucket, $path, $num = 20, $context = null) { 508 | $path = $this->cosUrlEncode($path); 509 | $expired = time() + self::EXPIRED_SECONDS; 510 | $url = $this->generateResUrl($bucket, $path); 511 | $signature = $this->auth->createReusableSignature($expired, $bucket); 512 | 513 | $data = array( 514 | 'op' => 'list', 515 | ); 516 | 517 | if ($num < 0 || $num > 199) { 518 | return array( 519 | 'code' => self::COSAPI_PARAMS_ERROR, 520 | 'message' => 'parameter num invalid, num need less then 200', 521 | ); 522 | } 523 | $data['num'] = $num; 524 | 525 | if (isset($context)) { 526 | $data['context'] = $context; 527 | } 528 | 529 | $url = $url . '?' . http_build_query($data); 530 | 531 | $req = array( 532 | 'url' => $url, 533 | 'method' => 'get', 534 | 'timeout' => $this->timeout, 535 | 'header' => array( 536 | 'Authorization: ' . $signature, 537 | ), 538 | ); 539 | 540 | return $this->sendRequest($req); 541 | } 542 | 543 | /* 544 | * 内部公共方法(更新文件和更新文件夹) 545 | * @param string $bucket bucket名称 546 | * @param string $path 路径 547 | * @param string $bizAttr 文件/目录属性 548 | * @param string $authority: eInvalid/eWRPrivate(私有)/eWPrivateRPublic(公有读写) 549 | * @param array $customer_headers_array 携带的用户自定义头域,包括 550 | * 'Cache-Control' => '*' 551 | * 'Content-Type' => '*' 552 | * 'Content-Disposition' => '*' 553 | * 'Content-Language' => '*' 554 | * 'x-cos-meta-自定义内容' => '*' 555 | */ 556 | private function updateBase( 557 | $bucket, $path, $bizAttr = null, $authority = null, $custom_headers_array = null) { 558 | 559 | $signature = $this->auth->createNonreusableSignature($bucket, $path); 560 | $path = $this->cosUrlEncode($path); 561 | $expired = time() + self::EXPIRED_SECONDS; 562 | $url = $this->generateResUrl($bucket, $path); 563 | 564 | $data = array('op' => 'update'); 565 | 566 | if (isset($bizAttr)) { 567 | $data['biz_attr'] = $bizAttr; 568 | } 569 | 570 | if (isset($authority) && strlen($authority) > 0) { 571 | if($this->isAuthorityValid($authority) == false) { 572 | return array( 573 | 'code' => self::COSAPI_PARAMS_ERROR, 574 | 'message' => 'parameter authority invalid'); 575 | } 576 | 577 | $data['authority'] = $authority; 578 | } 579 | 580 | if (isset($custom_headers_array)) { 581 | $data['custom_headers'] = array(); 582 | $this->add_customer_header($data['custom_headers'], $custom_headers_array); 583 | } 584 | 585 | $data = json_encode($data); 586 | 587 | $req = array( 588 | 'url' => $url, 589 | 'method' => 'post', 590 | 'timeout' => $this->timeout, 591 | 'data' => $data, 592 | 'header' => array( 593 | 'Authorization: ' . $signature, 594 | 'Content-Type: application/json', 595 | ), 596 | ); 597 | 598 | return $this->sendRequest($req); 599 | } 600 | 601 | /* 602 | * 内部方法 603 | * @param string $bucket bucket名称 604 | * @param string $path 文件/目录路径 605 | */ 606 | private function statBase($bucket, $path) { 607 | $path = $this->cosUrlEncode($path); 608 | $expired = time() + self::EXPIRED_SECONDS; 609 | $url = $this->generateResUrl($bucket, $path); 610 | $signature = $this->auth->createReusableSignature($expired, $bucket); 611 | 612 | $data = array('op' => 'stat'); 613 | 614 | $url = $url . '?' . http_build_query($data); 615 | 616 | $req = array( 617 | 'url' => $url, 618 | 'method' => 'get', 619 | 'timeout' => $this->timeout, 620 | 'header' => array( 621 | 'Authorization: ' . $signature, 622 | ), 623 | ); 624 | 625 | return $this->sendRequest($req); 626 | } 627 | 628 | /* 629 | * 内部私有方法 630 | * @param string $bucket bucket名称 631 | * @param string $path 文件/目录路径路径 632 | */ 633 | private function delBase($bucket, $path) { 634 | if ($path == "/") { 635 | return array( 636 | 'code' => self::COSAPI_PARAMS_ERROR, 637 | 'message' => 'can not delete bucket using api! go to ' . 638 | 'http://console.qcloud.com/cos to operate bucket'); 639 | } 640 | 641 | 642 | $signature = $this->auth->createNonreusableSignature($bucket, $path); 643 | $path = $this->cosUrlEncode($path); 644 | $expired = time() + self::EXPIRED_SECONDS; 645 | $url = $this->generateResUrl($bucket, $path); 646 | $data = array('op' => 'delete'); 647 | 648 | $data = json_encode($data); 649 | 650 | $req = array( 651 | 'url' => $url, 652 | 'method' => 'post', 653 | 'timeout' => $this->timeout, 654 | 'data' => $data, 655 | 'header' => array( 656 | 'Authorization: ' . $signature, 657 | 'Content-Type: application/json', 658 | ), 659 | ); 660 | 661 | return $this->sendRequest($req); 662 | } 663 | 664 | /* 665 | * 内部公共方法, 路径编码 666 | * @param string $path 待编码路径 667 | */ 668 | private function cosUrlEncode($path) { 669 | return str_replace('%2F', '/', rawurlencode($path)); 670 | } 671 | 672 | /* 673 | * 内部公共方法, 构造URL 674 | * @param string $bucket 675 | * @param string $dstPath 676 | */ 677 | private function generateResUrl($bucket, $dstPath) { 678 | $endPoint = str_replace('region', $this->region, $this->endPoint); 679 | 680 | return $endPoint . $this->config['app_id'] . '/' . $bucket . $dstPath; 681 | } 682 | 683 | /* 684 | * 内部公共方法, 发送消息 685 | * @param string $req 686 | */ 687 | private function sendRequest($req) { 688 | $rsp = $this->httpClient->sendRequest($req); 689 | if ($rsp === false) { 690 | return array( 691 | 'code' => self::COSAPI_NETWORK_ERROR, 692 | 'message' => 'network error', 693 | ); 694 | } 695 | 696 | $info = $this->httpClient->info(); 697 | $ret = json_decode($rsp, true); 698 | 699 | if ($ret === NULL) { 700 | return array( 701 | 'code' => self::COSAPI_NETWORK_ERROR, 702 | 'message' => $rsp, 703 | 'data' => array() 704 | ); 705 | } 706 | 707 | return $ret; 708 | } 709 | 710 | /** 711 | * Get slice size. 712 | */ 713 | private function getSliceSize($sliceSize) { 714 | // Fix slice size to 1MB. 715 | return self::SLICE_SIZE_1M; 716 | } 717 | 718 | /* 719 | * 内部方法, 规整文件路径 720 | * @param string $path 文件路径 721 | * @param string $isfolder 是否为文件夹 722 | */ 723 | private function normalizerPath($path, $isfolder = False) { 724 | if (preg_match('/^\//', $path) == 0) { 725 | $path = '/' . $path; 726 | } 727 | 728 | if ($isfolder == True) { 729 | if (preg_match('/\/$/', $path) == 0) { 730 | $path = $path . '/'; 731 | } 732 | } 733 | 734 | // Remove unnecessary slashes. 735 | $path = preg_replace('#/+#', '/', $path); 736 | 737 | return $path; 738 | } 739 | 740 | /** 741 | * 判断authority值是否正确 742 | * @param string $authority 743 | * @return [type] bool 744 | */ 745 | private function isAuthorityValid($authority) { 746 | if ($authority == 'eInvalid' || $authority == 'eWRPrivate' || $authority == 'eWPrivateRPublic') { 747 | return true; 748 | } 749 | return false; 750 | } 751 | 752 | /** 753 | * 判断是否符合自定义属性 754 | * @param string $key 755 | * @return [type] bool 756 | */ 757 | private function isCustomer_header($key) { 758 | if ($key == 'Cache-Control' || $key == 'Content-Type' || 759 | $key == 'Content-Disposition' || $key == 'Content-Language' || 760 | $key == 'Content-Encoding' || 761 | substr($key,0,strlen('x-cos-meta-')) == 'x-cos-meta-') { 762 | return true; 763 | } 764 | return false; 765 | } 766 | 767 | /** 768 | * 增加自定义属性到data中 769 | * @param array $data 770 | * @param array $customer_headers_array 771 | * @return [type] void 772 | */ 773 | private function add_customer_header(&$data, &$customer_headers_array) { 774 | if (count($customer_headers_array) < 1) { 775 | return; 776 | } 777 | foreach($customer_headers_array as $key=>$value) { 778 | if($this->isCustomer_header($key)) { 779 | $data[$key] = $value; 780 | } 781 | } 782 | } 783 | 784 | // Check |$path| is a valid file path. 785 | // Return true on success, otherwise return false. 786 | private function isValidPath($path) { 787 | if (strpos($path, '?') !== false) { 788 | return false; 789 | } 790 | if (strpos($path, '*') !== false) { 791 | return false; 792 | } 793 | if (strpos($path, ':') !== false) { 794 | return false; 795 | } 796 | if (strpos($path, '|') !== false) { 797 | return false; 798 | } 799 | if (strpos($path, '\\') !== false) { 800 | return false; 801 | } 802 | if (strpos($path, '<') !== false) { 803 | return false; 804 | } 805 | if (strpos($path, '>') !== false) { 806 | return false; 807 | } 808 | if (strpos($path, '"') !== false) { 809 | return false; 810 | } 811 | 812 | return true; 813 | } 814 | 815 | /** 816 | * Copy a file. 817 | * @param $bucket bucket name. 818 | * @param $srcFpath source file path. 819 | * @param $dstFpath destination file path. 820 | * @param $overwrite if the destination location is occupied, overwrite it or not? 821 | * @return array|mixed. 822 | */ 823 | public function copyFile($bucket, $srcFpath, $dstFpath, $overwrite = false) { 824 | $srcFpath = $this->normalizerPath($srcFpath, false); 825 | $srcFpath = $this->cosUrlEncode($srcFpath); 826 | $url = $this->generateResUrl($bucket, $srcFpath); 827 | $sign = $this->auth->createNonreusableSignature($bucket, $srcFpath); 828 | $data = array( 829 | 'op' => 'copy', 830 | 'dest_fileid' => $dstFpath, 831 | 'to_over_write' => $overwrite ? 1 : 0, 832 | ); 833 | $req = array( 834 | 'url' => $url, 835 | 'method' => 'post', 836 | 'timeout' => $this->timeout, 837 | 'data' => $data, 838 | 'header' => array( 839 | 'Authorization: ' . $sign, 840 | ), 841 | ); 842 | 843 | return $this->sendRequest($req); 844 | } 845 | 846 | /** 847 | * Move a file. 848 | * @param $bucket bucket name. 849 | * @param $srcFpath source file path. 850 | * @param $dstFpath destination file path. 851 | * @param $overwrite if the destination location is occupied, overwrite it or not? 852 | * @return array|mixed. 853 | */ 854 | public function moveFile($bucket, $srcFpath, $dstFpath, $overwrite = false) { 855 | $srcFpath = $this->normalizerPath($srcFpath, false); 856 | $srcFpath = $this->cosUrlEncode($srcFpath); 857 | $url = $this->generateResUrl($bucket, $srcFpath); 858 | $sign = $this->auth->createNonreusableSignature($bucket, $srcFpath); 859 | $data = array( 860 | 'op' => 'move', 861 | 'dest_fileid' => $dstFpath, 862 | 'to_over_write' => $overwrite ? 1 : 0, 863 | ); 864 | $req = array( 865 | 'url' => $url, 866 | 'method' => 'post', 867 | 'timeout' => $this->timeout, 868 | 'data' => $data, 869 | 'header' => array( 870 | 'Authorization: ' . $sign, 871 | ), 872 | ); 873 | 874 | return $this->sendRequest($req); 875 | } 876 | 877 | /** 878 | * Get file's url for downloading. 879 | * @param $bucket bucket name. 880 | * @param $fpath file path. 881 | * @param $expireAfterSecs url will expire after this secconds. 882 | * @return array|mixed. 883 | */ 884 | public function getDownloadUrl($bucket, $fpath, $expireAfterSecs) { 885 | $fpath = $this->normalizerPath($fpath, false); 886 | $expiration = time() + $expireAfterSecs; 887 | $signature = $this->auth->createReusableSignature($expiration, $bucket); 888 | $appId = $this->config['app_id']; 889 | $region = $this->config['region']; 890 | 891 | $accessUrl = "http://$bucket-$appId.file.myqcloud.com$fpath?sign=$signature"; 892 | $sourceUrl = "http://$bucket-$appId.cos${region}.myqcloud.com$fpath?sign=$signature"; 893 | $url = "http://$region.file.myqcloud.com/files/v2/${appId}${fpath}?sign=$signature"; 894 | 895 | return array( 896 | 'code' => 0, 897 | 'message' => 'SUCCESS', 898 | 'data' => array( 899 | 'access_url' => $accessUrl, 900 | 'source_url' => $sourceUrl, 901 | 'url' => $url, 902 | ), 903 | ); 904 | } 905 | } 906 | -------------------------------------------------------------------------------- /src/qcloud/cos/Auth.php: -------------------------------------------------------------------------------- 1 | appId = $appId; 20 | $this->secretId = $secretId; 21 | $this->secretKey = $secretKey; 22 | } 23 | 24 | /** 25 | * Create reusable signature for listDirectory in $bucket or uploadFile into $bucket. 26 | * If $filepath is not null, this signature will be binded with this $filepath. 27 | * This signature will expire at $expiration timestamp. 28 | * Return the signature on success. 29 | * Return error code if parameter is not valid. 30 | */ 31 | public function createReusableSignature($expiration, $bucket, $filepath = null, $uploadFlag = true) { 32 | $appId = $this->appId; 33 | $secretId = $this->secretId; 34 | $secretKey = $this->secretKey; 35 | 36 | if (empty($appId) || empty($secretId) || empty($secretKey)) { 37 | return self::AUTH_SECRET_ID_KEY_ERROR; 38 | } 39 | 40 | if (empty($filepath)) { 41 | return $this->createSignature($appId, $secretId, $secretKey, $expiration, $bucket, null); 42 | } else { 43 | if (preg_match('/^\//', $filepath) == 0) { 44 | $filepath = '/' . $filepath; 45 | } 46 | 47 | if ($uploadFlag) { 48 | $fileId = '/' . $appId . '/' . $bucket . $filepath; 49 | } else { 50 | $fileId = $filepath; 51 | } 52 | return $this->createSignature($appId, $secretId, $secretKey, $expiration, $bucket, $fileId); 53 | } 54 | } 55 | 56 | /** 57 | * Create nonreusable signature for delete $filepath in $bucket. 58 | * This signature will expire after single usage. 59 | * Return the signature on success. 60 | * Return error code if parameter is not valid. 61 | */ 62 | public function createNonreusableSignature($bucket, $filepath) { 63 | $appId = $this->appId; 64 | $secretId = $this->secretId; 65 | $secretKey = $this->secretKey; 66 | 67 | if (empty($appId) || empty($secretId) || empty($secretKey)) { 68 | return self::AUTH_SECRET_ID_KEY_ERROR; 69 | } 70 | 71 | if (preg_match('/^\//', $filepath) == 0) { 72 | $filepath = '/' . $filepath; 73 | } 74 | 75 | $fileId = '/' . $appId . '/' . $bucket . $filepath; 76 | return $this->createSignature($appId, $secretId, $secretKey, 0, $bucket, $fileId); 77 | } 78 | 79 | /** 80 | * A helper function for creating signature. 81 | * Return the signature on success. 82 | * Return error code if parameter is not valid. 83 | */ 84 | private function createSignature( 85 | $appId, $secretId, $secretKey, $expiration, $bucket, $fileId) { 86 | if (empty($secretId) || empty($secretKey)) { 87 | return self::AUTH_SECRET_ID_KEY_ERROR; 88 | } 89 | 90 | $now = time(); 91 | $random = rand(); 92 | $fileId = $this->encodeKey($fileId); 93 | $plainText = "a=$appId&b=$bucket&k=$secretId&e=$expiration&t=$now&r=$random&f=$fileId"; 94 | $bin = hash_hmac('SHA1', $plainText, $secretKey, true); 95 | $bin = $bin.$plainText; 96 | 97 | $signature = base64_encode($bin); 98 | 99 | return $signature; 100 | } 101 | public static function encodeKey($key) { 102 | return str_replace('%2F', '/', rawurlencode($key)); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/qcloud/cos/Helper.php: -------------------------------------------------------------------------------- 1 | value fields to post. 9 | * @return $boundary and encoded post fields. 10 | */ 11 | public static function buildCustomPostFields($fields) { 12 | // invalid characters for "name" and "filename" 13 | static $disallow = array("\0", "\"", "\r", "\n"); 14 | 15 | // initialize body 16 | $body = array(); 17 | 18 | // build normal parameters 19 | foreach ($fields as $key => $value) { 20 | $key = str_replace($disallow, "_", $key); 21 | $body[] = implode("\r\n", array( 22 | "Content-Disposition: form-data; name=\"{$key}\"", 23 | '', 24 | filter_var($value), 25 | )); 26 | } 27 | 28 | // generate safe boundary 29 | do { 30 | $boundary = "---------------------" . md5(mt_rand() . microtime()); 31 | } while (preg_grep("/{$boundary}/", $body)); 32 | 33 | // add boundary for each parameters 34 | foreach ($body as &$part) { 35 | $part = "--{$boundary}\r\n{$part}"; 36 | } 37 | unset($part); 38 | 39 | // add final boundary 40 | $body[] = "--{$boundary}--"; 41 | $body[] = ''; 42 | 43 | return array($boundary, implode("\r\n", $body)); 44 | } 45 | } -------------------------------------------------------------------------------- /src/qcloud/cos/HttpClient.php: -------------------------------------------------------------------------------- 1 | curlHandler) { 24 | if (function_exists('curl_reset')) { 25 | curl_reset($this->curlHandler); 26 | } else { 27 | $this->reset(); 28 | } 29 | } else { 30 | $this->curlHandler = curl_init(); 31 | } 32 | 33 | curl_setopt($this->curlHandler, CURLOPT_URL, $request['url']); 34 | 35 | $method = 'GET'; 36 | if (isset($request['method']) && 37 | in_array(strtolower($request['method']), array('get', 'post', 'put', 'delete', 'head'))) { 38 | $method = strtoupper($request['method']); 39 | } else if (isset($request['data'])) { 40 | $method = 'POST'; 41 | } 42 | 43 | $header = isset($request['header']) ? $request['header'] : array(); 44 | $header[] = 'Method:'.$method; 45 | $header[] = 'User-Agent:'.$this->getUserAgent(); 46 | $header[] = 'Connection: keep-alive'; 47 | 48 | isset($request['host']) && $header[] = 'Host:' . $request['host']; 49 | curl_setopt($this->curlHandler, CURLOPT_RETURNTRANSFER, 1); 50 | curl_setopt($this->curlHandler, CURLOPT_CUSTOMREQUEST, $method); 51 | isset($request['timeout']) && curl_setopt($this->curlHandler, CURLOPT_TIMEOUT, $request['timeout']); 52 | 53 | if (isset($request['data']) && in_array($method, array('POST', 'PUT'))) { 54 | if (defined('CURLOPT_SAFE_UPLOAD')) { 55 | curl_setopt($this->curlHandler, CURLOPT_SAFE_UPLOAD, true); 56 | } 57 | 58 | curl_setopt($this->curlHandler, CURLOPT_POST, true); 59 | array_push($header, 'Expect: 100-continue'); 60 | 61 | if (is_array($request['data'])) { 62 | $arr = Helper::buildCustomPostFields($request['data']); 63 | array_push($header, 'Content-Type: multipart/form-data; boundary=' . $arr[0]); 64 | curl_setopt($this->curlHandler, CURLOPT_POSTFIELDS, $arr[1]); 65 | } else { 66 | curl_setopt($this->curlHandler, CURLOPT_POSTFIELDS, $request['data']); 67 | } 68 | } 69 | curl_setopt($this->curlHandler, CURLOPT_HTTPHEADER, $header); 70 | 71 | $ssl = substr($request['url'], 0, 8) == "https://" ? true : false; 72 | if( isset($request['cert'])){ 73 | curl_setopt($this->curlHandler, CURLOPT_SSL_VERIFYPEER,true); 74 | curl_setopt($this->curlHandler, CURLOPT_CAINFO, $request['cert']); 75 | curl_setopt($this->curlHandler, CURLOPT_SSL_VERIFYHOST,2); 76 | if (isset($request['ssl_version'])) { 77 | curl_setopt($this->curlHandler, CURLOPT_SSLVERSION, $request['ssl_version']); 78 | } else { 79 | curl_setopt($this->curlHandler, CURLOPT_SSLVERSION, 4); 80 | } 81 | }else if( $ssl ){ 82 | curl_setopt($this->curlHandler, CURLOPT_SSL_VERIFYPEER,false); //true any ca 83 | curl_setopt($this->curlHandler, CURLOPT_SSL_VERIFYHOST, 0); //do not check 84 | if (isset($request['ssl_version'])) { 85 | curl_setopt($this->curlHandler, CURLOPT_SSLVERSION, $request['ssl_version']); 86 | } else { 87 | curl_setopt($this->curlHandler, CURLOPT_SSLVERSION, 4); 88 | } 89 | } 90 | $ret = curl_exec($this->curlHandler); 91 | $this->httpInfo = curl_getinfo($this->curlHandler); 92 | return $ret; 93 | } 94 | 95 | /** 96 | * 下载文件 97 | * @param array $request http请求信息 98 | * url : 请求的url地址 99 | * header : 需要设置的http头部 100 | * host : 请求头部host 101 | * timeout : 请求超时时间 102 | * cert : ca文件路径 103 | * ssl_version: SSL版本号 104 | * @param string $dstPath 下载保存文件 105 | * @return bool 下载是否成功 106 | */ 107 | public function download($request, $dstPath) { 108 | if ($this->curlHandler) { 109 | if (function_exists('curl_reset')) { 110 | curl_reset($this->curlHandler); 111 | } else { 112 | $this->reset(); 113 | } 114 | } else { 115 | $this->curlHandler = curl_init(); 116 | } 117 | 118 | curl_setopt($this->curlHandler, CURLOPT_URL, $request['url']); 119 | 120 | $method = 'GET'; 121 | 122 | $header = isset($request['header']) ? $request['header'] : array(); 123 | $header[] = 'Method:'.$method; 124 | $header[] = 'User-Agent:'.$this->getUserAgent(); 125 | $header[] = 'Connection: keep-alive'; 126 | 127 | isset($request['host']) && $header[] = 'Host:' . $request['host']; 128 | curl_setopt($this->curlHandler, CURLOPT_RETURNTRANSFER, 1); 129 | curl_setopt($this->curlHandler, CURLOPT_CUSTOMREQUEST, $method); 130 | isset($request['timeout']) && curl_setopt($this->curlHandler, CURLOPT_TIMEOUT, $request['timeout']); 131 | 132 | if (isset($request['data']) && in_array($method, array('POST', 'PUT'))) { 133 | if (defined('CURLOPT_SAFE_UPLOAD')) { 134 | curl_setopt($this->curlHandler, CURLOPT_SAFE_UPLOAD, true); 135 | } 136 | 137 | curl_setopt($this->curlHandler, CURLOPT_POST, true); 138 | array_push($header, 'Expect: 100-continue'); 139 | 140 | if (is_array($request['data'])) { 141 | $arr = Helper::buildCustomPostFields($request['data']); 142 | array_push($header, 'Content-Type: multipart/form-data; boundary=' . $arr[0]); 143 | curl_setopt($this->curlHandler, CURLOPT_POSTFIELDS, $arr[1]); 144 | } else { 145 | curl_setopt($this->curlHandler, CURLOPT_POSTFIELDS, $request['data']); 146 | } 147 | } 148 | curl_setopt($this->curlHandler, CURLOPT_HTTPHEADER, $header); 149 | 150 | $ssl = substr($request['url'], 0, 8) == "https://" ? true : false; 151 | if( isset($request['cert'])){ 152 | curl_setopt($this->curlHandler, CURLOPT_SSL_VERIFYPEER,true); 153 | curl_setopt($this->curlHandler, CURLOPT_CAINFO, $request['cert']); 154 | curl_setopt($this->curlHandler, CURLOPT_SSL_VERIFYHOST,2); 155 | if (isset($request['ssl_version'])) { 156 | curl_setopt($this->curlHandler, CURLOPT_SSLVERSION, $request['ssl_version']); 157 | } else { 158 | curl_setopt($this->curlHandler, CURLOPT_SSLVERSION, 4); 159 | } 160 | }else if( $ssl ){ 161 | curl_setopt($this->curlHandler, CURLOPT_SSL_VERIFYPEER,false); //true any ca 162 | curl_setopt($this->curlHandler, CURLOPT_SSL_VERIFYHOST,1); //check only host 163 | if (isset($request['ssl_version'])) { 164 | curl_setopt($this->curlHandler, CURLOPT_SSLVERSION, $request['ssl_version']); 165 | } else { 166 | curl_setopt($this->curlHandler, CURLOPT_SSLVERSION, 4); 167 | } 168 | } 169 | 170 | $fp = fopen($dstPath, 'wb'); 171 | if (!$fp) { 172 | return array( 173 | 'code' => Api::COSAPI_PARAMS_ERROR, 174 | 'message' => "Cannot open file {$dstPath}" 175 | ); 176 | } 177 | curl_setopt($this->curlHandler, CURLOPT_FILE, $fp); 178 | 179 | $ret = curl_exec($this->curlHandler); 180 | if (!$ret) { 181 | return array( 182 | 'code' => Api::COSAPI_NETWORK_ERROR, 183 | 'message' => "Download faild." 184 | ); 185 | } 186 | $this->httpInfo = curl_getinfo($this->curlHandler); 187 | fclose($fp); 188 | return array( 189 | 'code' => Api::COSAPI_SUCCESS, 190 | 'message' => "Download success." 191 | ); 192 | } 193 | 194 | public function info() { 195 | return $this->httpInfo; 196 | } 197 | 198 | private function getUserAgent() { 199 | return 'cos-php-sdk-' . Api::VERSION; 200 | } 201 | 202 | private function reset() { 203 | curl_setopt($this->curlHandler, CURLOPT_URL, ''); 204 | curl_setopt($this->curlHandler, CURLOPT_HTTPHEADER, array()); 205 | curl_setopt($this->curlHandler, CURLOPT_POSTFIELDS, array()); 206 | curl_setopt($this->curlHandler, CURLOPT_TIMEOUT, 0); 207 | curl_setopt($this->curlHandler, CURLOPT_SSL_VERIFYPEER, false); 208 | curl_setopt($this->curlHandler, CURLOPT_SSL_VERIFYHOST, 0); 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /src/qcloud/cos/HttpRequest.php: -------------------------------------------------------------------------------- 1 | sequence = 0; 14 | $this->curlMultiHandle = curl_multi_init(); 15 | $this->idleCurlHandle = array(); 16 | } 17 | 18 | public function __destruct() { 19 | curl_multi_close($this->curlMultiHandle); 20 | foreach ($this->idleCurlHandle as $handle) { 21 | curl_close($handle); 22 | } 23 | $this->idleCurlHandle = array(); 24 | } 25 | 26 | public function startSendingRequest($httpRequest, $done) { 27 | $this->sequence += 1; 28 | 29 | if (count($this->idleCurlHandle) !== 0) { 30 | $curlHandle = array_pop($this->idleCurlHandle); 31 | } else { 32 | $curlHandle = curl_init(); 33 | if ($curlHandle === false) { 34 | return false; 35 | } 36 | } 37 | 38 | curl_setopt($curlHandle, CURLOPT_TIMEOUT_MS, $httpRequest->timeoutMs); 39 | curl_setopt($curlHandle, CURLOPT_URL, $httpRequest->url); 40 | curl_setopt($curlHandle, CURLOPT_HEADER, 1); 41 | curl_setopt($curlHandle, CURLOPT_RETURNTRANSFER, 1); 42 | $headers = $httpRequest->customHeaders; 43 | array_push($headers, 'User-Agent:'.$this->getUserAgent()); 44 | if ($httpRequest->method === 'POST') { 45 | if (defined('CURLOPT_SAFE_UPLOAD')) { 46 | curl_setopt($curlHandle, CURLOPT_SAFE_UPLOAD, true); 47 | } 48 | 49 | curl_setopt($curlHandle, CURLOPT_POST, true); 50 | $arr = Helper::buildCustomPostFields($httpRequest->dataToPost); 51 | array_push($headers, 'Expect: 100-continue'); 52 | array_push($headers, 'Content-Type: multipart/form-data; boundary=' . $arr[0]); 53 | curl_setopt($curlHandle, CURLOPT_POSTFIELDS, $arr[1]); 54 | } 55 | curl_setopt($curlHandle, CURLOPT_HTTPHEADER, $headers); 56 | 57 | curl_multi_add_handle($this->curlMultiHandle, $curlHandle); 58 | 59 | 60 | $this->curlHandleInfo[$this->sequence]['handle'] = $curlHandle; 61 | $this->curlHandleInfo[$this->sequence]['done'] = $done; 62 | $this->curlHandleInfo[$this->sequence]['request'] = $httpRequest; 63 | } 64 | 65 | public function performSendingRequest() { 66 | for (;;) { 67 | $active = null; 68 | 69 | do { 70 | $mrc = curl_multi_exec($this->curlMultiHandle, $active); 71 | $info = curl_multi_info_read($this->curlMultiHandle); 72 | if ($info !== false) { 73 | $this->processResult($info); 74 | } 75 | } while ($mrc == CURLM_CALL_MULTI_PERFORM); 76 | 77 | while ($active && $mrc == CURLM_OK) { 78 | if (curl_multi_select($this->curlMultiHandle) == -1) { 79 | usleep(1); 80 | } 81 | 82 | do { 83 | $mrc = curl_multi_exec($this->curlMultiHandle, $active); 84 | $info = curl_multi_info_read($this->curlMultiHandle); 85 | if ($info !== false) { 86 | $this->processResult($info); 87 | } 88 | } while ($mrc == CURLM_CALL_MULTI_PERFORM); 89 | } 90 | 91 | if (count($this->curlHandleInfo) == 0) { 92 | break; 93 | } 94 | } 95 | } 96 | 97 | private function processResult($info) { 98 | $result = $info['result']; 99 | $handle = $info['handle']; 100 | $sequence = 0; 101 | 102 | foreach ($this->curlHandleInfo as $key => $info) { 103 | if ($info['handle'] === $handle) { 104 | $sequence = $key; 105 | break; 106 | } 107 | } 108 | 109 | $request = $this->curlHandleInfo[$sequence]['request']; 110 | $done = $this->curlHandleInfo[$sequence]['done']; 111 | $response = new HttpResponse(); 112 | 113 | if ($result !== CURLE_OK) { 114 | $response->curlErrorCode = $result; 115 | $response->curlErrorMessage = curl_error($handle); 116 | 117 | call_user_func($done, $request, $response); 118 | } else { 119 | $responseStr = curl_multi_getcontent($handle); 120 | $headerSize = curl_getinfo($handle, CURLINFO_HEADER_SIZE); 121 | $headerStr = substr($responseStr, 0, $headerSize); 122 | $body = substr($responseStr, $headerSize); 123 | 124 | $response->curlErrorCode = curl_errno($handle); 125 | $response->curlErrorMessage = curl_error($handle); 126 | $response->statusCode = curl_getinfo($handle, CURLINFO_HTTP_CODE); 127 | $headLines = explode("\r\n", $headerStr); 128 | foreach ($headLines as $head) { 129 | $arr = explode(':', $head); 130 | if (count($arr) >= 2) { 131 | $response->headers[trim($arr[0])] = trim($arr[1]); 132 | } 133 | } 134 | $response->body = $body; 135 | 136 | call_user_func($done, $request, $response); 137 | } 138 | 139 | unset($this->curlHandleInfo[$sequence]); 140 | curl_multi_remove_handle($this->curlMultiHandle, $handle); 141 | 142 | array_push($this->idleCurlHandle, $handle); 143 | } 144 | 145 | private function resetCurl($handle) { 146 | if (function_exists('curl_reset')) { 147 | curl_reset($handle); 148 | } else { 149 | curl_setopt($handle, CURLOPT_URL, ''); 150 | curl_setopt($handle, CURLOPT_HTTPHEADER, array()); 151 | curl_setopt($handle, CURLOPT_POSTFIELDS, array()); 152 | curl_setopt($handle, CURLOPT_TIMEOUT, 0); 153 | curl_setopt($handle, CURLOPT_SSL_VERIFYPEER, false); 154 | curl_setopt($handle, CURLOPT_SSL_VERIFYHOST, 0); 155 | } 156 | } 157 | 158 | private function getUserAgent() { 159 | return 'cos-php-sdk-' . Api::VERSION; 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /src/qcloud/cos/SliceUploading.php: -------------------------------------------------------------------------------- 1 | timeoutMs = $timeoutMs; 40 | $this->maxRetryCount = $maxRetryCount; 41 | $this->errorCode = Api::COSAPI_SUCCESS; 42 | $this->errorMessage = ''; 43 | $this->concurrentTaskNumber = self::DEFAULT_CONCURRENT_TASK_NUMBER; 44 | 45 | $this->offset = 0; 46 | 47 | $this->libcurlWrapper = new LibcurlWrapper(); 48 | $this->httpClient = new HttpClient(); 49 | } 50 | 51 | public function __destruct() { 52 | } 53 | 54 | public function getLastErrorCode() { 55 | return $this->errorCode; 56 | } 57 | 58 | public function getLastErrorMessage() { 59 | return $this->errorMessage; 60 | } 61 | 62 | public function getRequestId() { 63 | return $this->requestId; 64 | } 65 | 66 | public function getAccessUrl() { 67 | return $this->accessUrl; 68 | } 69 | 70 | public function getResourcePath() { 71 | return $this->resourcePath; 72 | } 73 | 74 | public function getSourceUrl() { 75 | return $this->sourceUrl; 76 | } 77 | 78 | /** 79 | * Return true on success and return false on failure. 80 | */ 81 | public function initUploading( 82 | $signature, $srcFpath, $url, $fileSize, $sliceSize, $bizAttr, $insertOnly) { 83 | $this->signature = $signature; 84 | $this->srcFpath = $srcFpath; 85 | $this->url = $url; 86 | $this->fileSize = $fileSize; 87 | $this->sliceSize = $sliceSize; 88 | 89 | // Clear error so caller can successfully retry. 90 | $this->clearError(); 91 | 92 | $request = array( 93 | 'url' => $url, 94 | 'method' => 'post', 95 | 'timeout' => $this->timeoutMs / 1000, 96 | 'data' => array( 97 | 'op' => 'upload_slice_init', 98 | 'filesize' => $fileSize, 99 | 'slice_size' => $sliceSize, 100 | 'insertOnly' => $insertOnly, 101 | ), 102 | 'header' => array( 103 | 'Authorization: ' . $signature, 104 | ), 105 | ); 106 | 107 | if (isset($bizAttr) && strlen($bizAttr)) { 108 | $request['data']['biz_attr'] = $bizAttr; 109 | } 110 | 111 | $response = $this->sendRequest($request); 112 | if ($response === false) { 113 | return false; 114 | } 115 | $this->session = $response['data']['session']; 116 | 117 | if (isset($response['data']['slice_size'])) { 118 | $this->sliceSize = $response['data']['slice_size']; 119 | } 120 | 121 | if (isset($response['data']['serial_upload']) && $response['data']['serial_upload'] == 1) { 122 | $this->concurrentTaskNumber = 1; 123 | } 124 | 125 | return true; 126 | } 127 | 128 | /** 129 | * Return true on success and return false on failure. 130 | */ 131 | public function performUploading() { 132 | for ($i = 0; $i < $this->concurrentTaskNumber; ++$i) { 133 | if ($this->offset >= $this->fileSize) { 134 | break; 135 | } 136 | 137 | $sliceContent = file_get_contents($this->srcFpath, false, null, $this->offset, $this->sliceSize); 138 | if ($sliceContent === false) { 139 | $this->setError(COSAPI_PARAMS_ERROR, 'read file ' . $this->srcFpath . ' error'); 140 | return false; 141 | } 142 | 143 | $request = new HttpRequest(); 144 | $request->timeoutMs = $this->timeoutMs; 145 | $request->url = $this->url; 146 | $request->method = 'POST'; 147 | $request->customHeaders = array( 148 | 'Authorization: ' . $this->signature, 149 | ); 150 | $request->dataToPost = array( 151 | 'op' => 'upload_slice_data', 152 | 'session' => $this->session, 153 | 'offset' => $this->offset, 154 | 'filecontent' => $sliceContent, 155 | 'datamd5' => md5($sliceContent), 156 | ); 157 | $request->userData = array( 158 | 'retryCount' => 0, 159 | ); 160 | 161 | $this->libcurlWrapper->startSendingRequest($request, array($this, 'uploadCallback')); 162 | 163 | $this->offset += $this->sliceSize; 164 | } 165 | 166 | $this->libcurlWrapper->performSendingRequest(); 167 | 168 | if ($this->errorCode !== Api::COSAPI_SUCCESS) { 169 | return false; 170 | } 171 | 172 | return true; 173 | } 174 | 175 | /** 176 | * Return true on success and return false on failure. 177 | */ 178 | public function finishUploading() { 179 | $request = array( 180 | 'url' => $this->url, 181 | 'method' => 'post', 182 | 'timeout' => $this->timeoutMs / 1000, 183 | 'data' => array( 184 | 'op' => 'upload_slice_finish', 185 | 'session' => $this->session, 186 | 'filesize' => $this->fileSize, 187 | ), 188 | 'header' => array( 189 | 'Authorization: ' . $this->signature, 190 | ), 191 | ); 192 | 193 | $response = $this->sendRequest($request); 194 | if ($response === false) { 195 | return false; 196 | } 197 | 198 | $this->accessUrl = $response['data']['access_url']; 199 | $this->resourcePath = $response['data']['resource_path']; 200 | $this->sourceUrl = $response['data']['source_url']; 201 | 202 | return true; 203 | } 204 | 205 | private function sendRequest($request) { 206 | $response = $this->httpClient->sendRequest($request); 207 | if ($response === false) { 208 | $this->setError(Api::COSAPI_NETWORK_ERROR, 'network error'); 209 | return false; 210 | } 211 | 212 | $responseJson = json_decode($response, true); 213 | if ($responseJson === NULL) { 214 | $this->setError(Api::COSAPI_NETWORK_ERROR, 'network error'); 215 | return false; 216 | } 217 | 218 | $this->requestId = $responseJson['request_id']; 219 | if ($responseJson['code'] != 0) { 220 | $this->setError($responseJson['code'], $responseJson['message']); 221 | return false; 222 | } 223 | 224 | return $responseJson; 225 | } 226 | 227 | private function clearError() { 228 | $this->errorCode = Api::COSAPI_SUCCESS; 229 | $this->errorMessage = 'success'; 230 | } 231 | 232 | private function setError($errorCode, $errorMessage) { 233 | $this->errorCode = $errorCode; 234 | $this->errorMessage = $errorMessage; 235 | } 236 | 237 | public function uploadCallback($request, $response) { 238 | if ($this->errorCode !== Api::COSAPI_SUCCESS) { 239 | return; 240 | } 241 | 242 | $requestErrorCode = Api::COSAPI_SUCCESS; 243 | $requestErrorMessage = 'success'; 244 | $retryCount = $request->userData['retryCount']; 245 | 246 | $responseJson = json_decode($response->body, true); 247 | if ($responseJson === NULL) { 248 | $requestErrorCode = Api::COSAPI_NETWORK_ERROR; 249 | $requestErrorMessage = 'network error'; 250 | } 251 | 252 | if ($response->curlErrorCode !== CURLE_OK) { 253 | $requestErrorCode = Api::COSAPI_NETWORK_ERROR; 254 | $requestErrorMessage = 'network error: curl errno ' . $response->curlErrorCode; 255 | } 256 | 257 | $this->requestId = $responseJson['request_id']; 258 | if ($responseJson['code'] != 0) { 259 | $requestErrorCode = $responseJson['code']; 260 | $requestErrorMessage = $responseJson['message']; 261 | } 262 | 263 | if (isset($responseJson['data']['datamd5']) && 264 | $responseJson['data']['datamd5'] !== $request->dataToPost['datamd5']) { 265 | $requestErrorCode = Api::COSAPI_INTEGRITY_ERROR; 266 | $requestErrorMessage = 'cosapi integrity error'; 267 | } 268 | 269 | if ($requestErrorCode !== Api::COSAPI_SUCCESS) { 270 | if ($retryCount >= $this->maxRetryCount) { 271 | $this->setError($requestErrorCode, $requestErrorMessage); 272 | } else { 273 | $request->userData['retryCount'] += 1; 274 | $this->libcurlWrapper->startSendingRequest($request, array($this, 'uploadCallback')); 275 | } 276 | return; 277 | } 278 | 279 | if ($this->offset >= $this->fileSize) { 280 | return; 281 | } 282 | 283 | // Send next slice. 284 | $nextSliceContent = file_get_contents($this->srcFpath, false, null, $this->offset, $this->sliceSize); 285 | if ($nextSliceContent === false) { 286 | $this->setError(COSAPI_PARAMS_ERROR, 'read file ' . $this->srcFpath . ' error'); 287 | return; 288 | } 289 | 290 | $nextSliceRequest = new HttpRequest(); 291 | $nextSliceRequest->timeoutMs = $this->timeoutMs; 292 | $nextSliceRequest->url = $this->url; 293 | $nextSliceRequest->method = 'POST'; 294 | $nextSliceRequest->customHeaders = array( 295 | 'Authorization: ' . $this->signature, 296 | ); 297 | $nextSliceRequest->dataToPost = array( 298 | 'op' => 'upload_slice_data', 299 | 'session' => $this->session, 300 | 'offset' => $this->offset, 301 | 'filecontent' => $nextSliceContent, 302 | 'datamd5' => md5($nextSliceContent), 303 | ); 304 | $nextSliceRequest->userData = array( 305 | 'retryCount' => 0, 306 | ); 307 | 308 | $this->libcurlWrapper->startSendingRequest($nextSliceRequest, array($this, 'uploadCallback')); 309 | 310 | $this->offset += $this->sliceSize; 311 | } 312 | } 313 | -------------------------------------------------------------------------------- /src/qcloud/cos/Test.php: -------------------------------------------------------------------------------- 1 | '1252448703', 18 | 'secret_id' => getenv('COS_KEY'), 19 | 'secret_key' => getenv('COS_SECRET'), 20 | 'region' => getenv('COS_REGION_V4'), // bucket所属地域:华北 'tj' 华东 'sh' 华南 'gz' 21 | 'timeout' => 60 22 | ); 23 | $this->cosApi = new Api($config); 24 | $this->bucket = 'testbucketv4'. $config['region']; 25 | $this->cospath = '→↓←→↖↗↙↘! \"#$%&\'()*+,-./0123456789:;<=>@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~'; 26 | $this->localpath = "111"; 27 | $this->folder = "新建folder"; 28 | 29 | $file = $this->localpath; 30 | file_put_contents($file,"12345",FILE_APPEND); 31 | } 32 | 33 | 34 | protected function tearDown() { 35 | unlink ($this->localpath); 36 | } 37 | 38 | public function testUploadFile() { 39 | try { 40 | $rt = $this->cosApi->upload($this->bucket, $this->localpath, $this->cospath); 41 | $this->cosApi->delFile($this->bucket, $this->cospath); 42 | $this->assertEquals(0, $rt['code']); 43 | } catch (\Exception $e) { 44 | $this->assertFalse(true, $e); 45 | } 46 | } 47 | public function testStatFile() { 48 | try { 49 | $file = $this->localpath; 50 | file_put_contents($file,"12345",FILE_APPEND); 51 | $this->cosApi->upload($this->bucket, $this->localpath, $this->cospath); 52 | $rt = $this->cosApi->stat($this->bucket, $this->cospath); 53 | $this->cosApi->delFile($this->bucket, $this->cospath); 54 | $this->assertEquals(0, $rt['code']); 55 | $this->cosApi->delFile($this->bucket, $this->cospath); 56 | } catch (\Exception $e) { 57 | $this->assertFalse(true, $e); 58 | } 59 | } 60 | public function testDownloadFile() { 61 | try { 62 | $this->cosApi->upload($this->bucket, $this->localpath, $this->cospath); 63 | $rt = $this->cosApi->download($this->bucket, $this->cospath, $this->localpath); 64 | $this->cosApi->delFile($this->bucket, $this->cospath); 65 | $this->assertEquals(0, $rt['code']); 66 | } catch (\Exception $e) { 67 | $this->assertFalse(true, $e); 68 | } 69 | } 70 | public function testUpdateFileInfo() { 71 | try { 72 | $this->cosApi->upload($this->bucket, $this->localpath, $this->cospath); 73 | $bizAttr = ''; 74 | $authority = 'eWPrivateRPublic'; 75 | $customerHeaders = array( 76 | 'Cache-Control' => 'no', 77 | 'Content-Type' => 'application/pdf', 78 | 'Content-Language' => 'ch', 79 | ); 80 | $rt = $this->cosApi->update($this->bucket, $this->cospath, $bizAttr, $authority, $customerHeaders); 81 | $this->cosApi->delFile($this->bucket, $this->cospath); 82 | $this->assertEquals(0, $rt['code']); 83 | } catch (\Exception $e) { 84 | $this->assertFalse(true, $e); 85 | } 86 | } 87 | public function testCopyFile() { 88 | try { 89 | $this->cosApi->upload($this->bucket, $this->localpath, $this->cospath); 90 | $rt = $this->cosApi->copyFile($this->bucket, $this->cospath, $this->cospath . '_copy'); 91 | $this->cosApi->delFile($this->bucket, $this->cospath); 92 | $this->cosApi->delFile($this->bucket, $this->cospath . '_copy'); 93 | $this->assertEquals(0, $rt['code']); 94 | } catch (\Exception $e) { 95 | $this->assertFalse(true, $e); 96 | } 97 | } 98 | public function testMoveFile() { 99 | try { 100 | $this->cosApi->upload($this->bucket, $this->localpath, $this->cospath); 101 | $rt = $this->cosApi->moveFile($this->bucket, $this->cospath, $this->cospath . '_move'); 102 | $this->cosApi->delFile($this->bucket, $this->cospath); 103 | $this->cosApi->delFile($this->bucket, $this->cospath . '_move'); 104 | $this->assertEquals(0, $rt['code']); 105 | } catch (\Exception $e) { 106 | $this->assertFalse(true, $e); 107 | } 108 | } 109 | public function testDeleteFile() { 110 | try { 111 | $this->cosApi->upload($this->bucket, $this->localpath, $this->cospath); 112 | $rt = $this->cosApi->delFile($this->bucket, $this->cospath); 113 | $this->assertEquals(0, $rt['code']); 114 | } catch (\Exception $e) { 115 | $this->assertFalse(true, $e); 116 | } 117 | } 118 | public function testCreateFolder() { 119 | try { 120 | $rt = $this->cosApi->createFolder($this->bucket, $this->folder); 121 | $rt = $this->cosApi->delFolder($this->bucket, $this->folder); 122 | $this->assertEquals(0, $rt['code']); 123 | } catch (\Exception $e) { 124 | $this->assertFalse(true, $e); 125 | } 126 | } 127 | 128 | public function testListFolder() { 129 | try { 130 | $this->cosApi->createFolder($this->bucket, $this->folder); 131 | $rt = $this->cosApi->listFolder($this->bucket, $this->folder); 132 | $rt = $this->cosApi->delFolder($this->bucket, $this->folder); 133 | $this->assertEquals(0, $rt['code']); 134 | } catch (\Exception $e) { 135 | $this->assertFalse(true, $e); 136 | } 137 | } 138 | 139 | public function testUpdateFolderInfo() { 140 | try { 141 | $bizAttr = ""; 142 | $this->cosApi->createFolder($this->bucket, $this->folder); 143 | $rt = $this->cosApi->updateFolder($this->bucket, $this->folder, $bizAttr); 144 | $rt = $this->cosApi->delFolder($this->bucket, $this->folder); 145 | $this->assertEquals(0, $rt['code']); 146 | } catch (\Exception $e) { 147 | $this->assertFalse(true, $e); 148 | } 149 | } 150 | public function testStatFolder() { 151 | try { 152 | $this->cosApi->createFolder($this->bucket, $this->folder); 153 | $rt = $this->cosApi->statFolder($this->bucket, $this->folder); 154 | $rt = $this->cosApi->delFolder($this->bucket, $this->folder); 155 | $this->assertEquals(0, $rt['code']); 156 | } catch (\Exception $e) { 157 | $this->assertFalse(true, $e); 158 | } 159 | } 160 | public function testDelteFolder() { 161 | try { 162 | $this->cosApi->createFolder($this->bucket, $this->folder); 163 | $rt = $this->cosApi->delFolder($this->bucket, $this->folder); 164 | $this->assertEquals(0, $rt['code']); 165 | } catch (\Exception $e) { 166 | $this->assertFalse(true, $e); 167 | } 168 | } 169 | } 170 | --------------------------------------------------------------------------------