Api.php 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905
  1. <?php
  2. namespace app\common\services\qcloud;
  3. class Api {
  4. //版本
  5. const VERSION = 'v4.3.11';
  6. //计算sign签名的时间参数
  7. const EXPIRED_SECONDS = 180;
  8. //1M
  9. const SLICE_SIZE_1M = 1048576;
  10. //20M 大于20M的文件需要进行分片传输
  11. const MAX_UNSLICE_FILE_SIZE = 20971520;
  12. //失败尝试次数
  13. const MAX_RETRY_TIMES = 3;
  14. //错误代码
  15. const COSAPI_SUCCESS = 0;
  16. const COSAPI_PARAMS_ERROR = -1;
  17. const COSAPI_NETWORK_ERROR = -2;
  18. const COSAPI_INTEGRITY_ERROR = -3;
  19. //HTTP请求超时时间
  20. private $timeout = 60;
  21. private $endPoint = 'http://region.file.myqcloud.com/files/v2/';
  22. private $region = 'gz'; // default region is guangzou
  23. private $auth;
  24. private $httpClient;
  25. private $config;
  26. public function __construct($config) {
  27. if (empty($config['app_id']) || empty($config['secret_id']) || empty($config['secret_key'])) {
  28. throw new \Exception('Config need app_id,secret_id,secret_key!');
  29. }
  30. $this->config = $config;
  31. $this->auth = new Auth($config['app_id'], $config['secret_id'], $config['secret_key']);
  32. $this->httpClient = new HttpClient();
  33. if (isset($config['region'])) {
  34. $this->setRegion($config['region']);
  35. }
  36. if (isset($config['timeout'])) {
  37. $this->setTimeout($config['timeout']);
  38. }
  39. }
  40. /**
  41. * 设置HTTP请求超时时间
  42. * @param int $timeout 超时时长
  43. */
  44. public function setTimeout($timeout = 60) {
  45. if (!is_int($timeout) || $timeout < 0) {
  46. return false;
  47. }
  48. $this->timeout = $timeout;
  49. return true;
  50. }
  51. public function setRegion($region) {
  52. $this->region = $region;
  53. }
  54. /**
  55. * 上传文件,自动判断文件大小,如果小于20M则使用普通文件上传,大于20M则使用分片上传
  56. * @param string $bucket bucket名称
  57. * @param string $srcPath 本地文件路径
  58. * @param string $dstPath 上传的文件路径
  59. * @param string $bizAttr 文件属性
  60. * @param string $slicesize 分片大小(512k,1m,2m,3m),默认:1m
  61. * @param string $insertOnly 同名文件是否覆盖
  62. * @return [type] [description]
  63. */
  64. public function upload(
  65. $bucket, $srcPath, $dstPath, $bizAttr=null, $sliceSize=null, $insertOnly=null) {
  66. if (!file_exists($srcPath)) {
  67. return array(
  68. 'code' => self::COSAPI_PARAMS_ERROR,
  69. 'message' => 'file ' . $srcPath .' not exists',
  70. 'data' => array()
  71. );
  72. }
  73. if (!$dstPath || !is_string($dstPath)
  74. || $dstPath[strlen($dstPath) - 1] == '/') {
  75. return array(
  76. 'code' => self::COSAPI_PARAMS_ERROR,
  77. 'message' => 'dstPath ' . $dstPath .' invalid',
  78. 'data' => array()
  79. );
  80. }
  81. $dstPath = $this->normalizerPath($dstPath, false);
  82. //文件大于20M则使用分片传输
  83. if (filesize($srcPath) < self::MAX_UNSLICE_FILE_SIZE ) {
  84. return $this->uploadFile($bucket, $srcPath, $dstPath, $bizAttr, $insertOnly);
  85. } else {
  86. $sliceSize = $this->getSliceSize($sliceSize);
  87. return $this->uploadBySlicing($bucket, $srcPath, $dstPath, $bizAttr, $sliceSize, $insertOnly);
  88. }
  89. }
  90. /* *
  91. * 上传内存中的内容
  92. * @param string $bucket bucket名称
  93. * @param string $content 文件内容,二进制安全
  94. * @param string $dstPath 上传的文件路径
  95. * @param string $bizAttr 文件属性
  96. * @param int $insertOnly 是否覆盖同名文件:0 覆盖,1:不覆盖
  97. *
  98. * */
  99. public function uploadBuffer(
  100. $bucket, $content, $dstPath,
  101. $bizAttr=null, $insertOnly=null) {
  102. if (strlen($content) >= self::MAX_UNSLICE_FILE_SIZE) {
  103. return array(
  104. 'code' => self::COSAPI_PARAMS_ERROR,
  105. 'message' => 'content larger then 20M, not supported',
  106. 'data' => array()
  107. );
  108. }
  109. if (!$dstPath || !is_string($dstPath)
  110. || $dstPath[strlen($dstPath) - 1] == '/') {
  111. return array(
  112. 'code' => self::COSAPI_PARAMS_ERROR,
  113. 'message' => 'dstPath ' . $dstPath .' invalid',
  114. 'data' => array()
  115. );
  116. }
  117. $dstPath = $this->cosUrlEncode($dstPath);
  118. $expired = time() + self::EXPIRED_SECONDS;
  119. $url = $this->generateResUrl($bucket, $dstPath);
  120. $signature = $this->auth->createReusableSignature($expired, $bucket);
  121. $fileSha = sha1($content);
  122. $data = array(
  123. 'op' => 'upload',
  124. 'sha' => $fileSha,
  125. 'biz_attr' => (isset($bizAttr) ? $bizAttr : ''),
  126. 'filecontent' => $content,
  127. );
  128. if (isset($insertOnly) && strlen($insertOnly) > 0) {
  129. $data['insertOnly'] = (($insertOnly == 0 || $insertOnly == '0' ) ? 0 : 1);
  130. }
  131. $req = array(
  132. 'url' => $url,
  133. 'method' => 'post',
  134. 'timeout' => $this->timeout,
  135. 'data' => $data,
  136. 'header' => array(
  137. 'Authorization: ' . $signature,
  138. ),
  139. );
  140. return $this->sendRequest($req);
  141. }
  142. /**
  143. * 下载文件
  144. * @param string $bucket bucket名称
  145. * @param string $srcPath 本地文件路径
  146. * @param string $dstPath 上传的文件路径
  147. * @return [type] [description]
  148. */
  149. public function download($bucket, $srcPath, $dstPath) {
  150. $srcInfo = $this->stat($bucket, $srcPath);
  151. if ($srcInfo['code'] !== 0) {
  152. return array(
  153. 'code' => self::COSAPI_PARAMS_ERROR,
  154. 'message' => 'file '.$srcPath.' does not exists.',
  155. 'data' => array()
  156. );
  157. }
  158. $url = $srcInfo['data']['source_url'];
  159. $sha = $srcInfo['data']['sha'];
  160. $expired = time() + self::EXPIRED_SECONDS;
  161. $signature = $this->auth->createReusableSignature($expired, $bucket);
  162. $req = array(
  163. 'url' => $url,
  164. 'method' => 'get',
  165. 'timeout' => $this->timeout,
  166. 'header' => array(
  167. 'Authorization: ' . $signature,
  168. ),
  169. );
  170. $result = $this->httpClient->download($req, $dstPath);
  171. if ($result['code'] !== self::COSAPI_SUCCESS) {
  172. return array(
  173. 'code' => $result['code'],
  174. 'message' => $result['message'],
  175. 'data' => array()
  176. );
  177. }
  178. return array(
  179. 'code' => self::COSAPI_SUCCESS,
  180. 'message' => 'SUCCESS',
  181. 'data' => array()
  182. );
  183. }
  184. /*
  185. * 创建目录
  186. * @param string $bucket bucket名称
  187. * @param string $folder 目录路径
  188. * @param string $bizAttr 目录属性
  189. */
  190. public function createFolder($bucket, $folder, $bizAttr = null) {
  191. if (!$this->isValidPath($folder)) {
  192. return array(
  193. 'code' => self::COSAPI_PARAMS_ERROR,
  194. 'message' => 'folder ' . $folder . ' is not a valid folder name',
  195. 'data' => array()
  196. );
  197. }
  198. $folder = $this->normalizerPath($folder, True);
  199. $folder = $this->cosUrlEncode($folder);
  200. $expired = time() + self::EXPIRED_SECONDS;
  201. $url = $this->generateResUrl($bucket, $folder);
  202. $signature = $this->auth->createReusableSignature($expired, $bucket);
  203. $data = array(
  204. 'op' => 'create',
  205. 'biz_attr' => (isset($bizAttr) ? $bizAttr : ''),
  206. );
  207. $data = json_encode($data);
  208. $req = array(
  209. 'url' => $url,
  210. 'method' => 'post',
  211. 'timeout' => $this->timeout,
  212. 'data' => $data,
  213. 'header' => array(
  214. 'Authorization: ' . $signature,
  215. 'Content-Type: application/json',
  216. ),
  217. );
  218. return $this->sendRequest($req);
  219. }
  220. /*
  221. * 目录列表
  222. * @param string $bucket bucket名称
  223. * @param string $path 目录路径,sdk会补齐末尾的 '/'
  224. * @param int $num 拉取的总数
  225. * @param string $offset 透传字段,用于翻页,前端不需理解,需要往前/往后翻页则透传回来
  226. */
  227. public function listFolder(
  228. $bucket, $folder, $num = 20,
  229. $context = null) {
  230. $folder = $this->normalizerPath($folder, True);
  231. return $this->listBase($bucket, $folder, $num, $context);
  232. }
  233. /*
  234. * 目录列表(前缀搜索)
  235. * @param string $bucket bucket名称
  236. * @param string $prefix 列出含此前缀的所有文件
  237. * @param int $num 拉取的总数
  238. * @param string $offset 透传字段,用于翻页,前端不需理解,需要往前/往后翻页则透传回来
  239. */
  240. public function prefixSearch(
  241. $bucket, $prefix, $num = 20,
  242. $context = null) {
  243. $path = $this->normalizerPath($prefix);
  244. return $this->listBase($bucket, $prefix, $num, $context);
  245. }
  246. /*
  247. * 目录更新
  248. * @param string $bucket bucket名称
  249. * @param string $folder 文件夹路径,SDK会补齐末尾的 '/'
  250. * @param string $bizAttr 目录属性
  251. */
  252. public function updateFolder($bucket, $folder, $bizAttr = null) {
  253. $folder = $this->normalizerPath($folder, True);
  254. return $this->updateBase($bucket, $folder, $bizAttr);
  255. }
  256. /*
  257. * 查询目录信息
  258. * @param string $bucket bucket名称
  259. * @param string $folder 目录路径
  260. */
  261. public function statFolder($bucket, $folder) {
  262. $folder = $this->normalizerPath($folder, True);
  263. return $this->statBase($bucket, $folder);
  264. }
  265. /*
  266. * 删除目录
  267. * @param string $bucket bucket名称
  268. * @param string $folder 目录路径
  269. * 注意不能删除bucket下根目录/
  270. */
  271. public function delFolder($bucket, $folder) {
  272. if (empty($bucket) || empty($folder)) {
  273. return array(
  274. 'code' => self::COSAPI_PARAMS_ERROR,
  275. 'message' => 'bucket or path is empty');
  276. }
  277. $folder = $this->normalizerPath($folder, True);
  278. return $this->delBase($bucket, $folder);
  279. }
  280. /*
  281. * 更新文件
  282. * @param string $bucket bucket名称
  283. * @param string $path 文件路径
  284. * @param string $authority: eInvalid(继承Bucket的读写权限)/eWRPrivate(私有读写)/eWPrivateRPublic(公有读私有写)
  285. * @param array $customer_headers_array 携带的用户自定义头域,包括
  286. * 'Cache-Control' => '*'
  287. * 'Content-Type' => '*'
  288. * 'Content-Disposition' => '*'
  289. * 'Content-Language' => '*'
  290. * 'x-cos-meta-自定义内容' => '*'
  291. */
  292. public function update($bucket, $path,
  293. $bizAttr = null, $authority=null,$customer_headers_array=null) {
  294. $path = $this->normalizerPath($path);
  295. return $this->updateBase($bucket, $path, $bizAttr, $authority, $customer_headers_array);
  296. }
  297. /*
  298. * 查询文件信息
  299. * @param string $bucket bucket名称
  300. * @param string $path 文件路径
  301. */
  302. public function stat($bucket, $path) {
  303. $path = $this->normalizerPath($path);
  304. return $this->statBase($bucket, $path);
  305. }
  306. /*
  307. * 删除文件
  308. * @param string $bucket
  309. * @param string $path 文件路径
  310. */
  311. public function delFile($bucket, $path) {
  312. if (empty($bucket) || empty($path)) {
  313. return array(
  314. 'code' => self::COSAPI_PARAMS_ERROR,
  315. 'message' => 'path is empty');
  316. }
  317. $path = $this->normalizerPath($path);
  318. return $this->delBase($bucket, $path);
  319. }
  320. /**
  321. * 内部方法, 上传文件
  322. * @param string $bucket bucket名称
  323. * @param string $srcPath 本地文件路径
  324. * @param string $dstPath 上传的文件路径
  325. * @param string $bizAttr 文件属性
  326. * @param int $insertOnly 是否覆盖同名文件:0 覆盖,1:不覆盖
  327. * @return [type] [description]
  328. */
  329. private function uploadFile($bucket, $srcPath, $dstPath, $bizAttr = null, $insertOnly = null) {
  330. $srcPath = realpath($srcPath);
  331. $dstPath = $this->cosUrlEncode($dstPath);
  332. if (filesize($srcPath) >= self::MAX_UNSLICE_FILE_SIZE ) {
  333. return array(
  334. 'code' => self::COSAPI_PARAMS_ERROR,
  335. 'message' => 'file '.$srcPath.' larger then 20M, please use uploadBySlicing interface',
  336. 'data' => array()
  337. );
  338. }
  339. $expired = time() + self::EXPIRED_SECONDS;
  340. $url = $this->generateResUrl($bucket, $dstPath);
  341. $signature = $this->auth->createReusableSignature($expired, $bucket);
  342. $fileSha = hash_file('sha1', $srcPath);
  343. $data = array(
  344. 'op' => 'upload',
  345. 'sha' => $fileSha,
  346. 'biz_attr' => (isset($bizAttr) ? $bizAttr : ''),
  347. );
  348. $data['filecontent'] = file_get_contents($srcPath);
  349. if (isset($insertOnly) && strlen($insertOnly) > 0) {
  350. $data['insertOnly'] = (($insertOnly == 0 || $insertOnly == '0' ) ? 0 : 1);
  351. }
  352. $req = array(
  353. 'url' => $url,
  354. 'method' => 'post',
  355. 'timeout' => $this->timeout,
  356. 'data' => $data,
  357. 'header' => array(
  358. 'Authorization: ' . $signature,
  359. ),
  360. );
  361. return $this->sendRequest($req);
  362. }
  363. /**
  364. * 内部方法,上传文件
  365. * @param string $bucket bucket名称
  366. * @param string $srcPath 本地文件路径
  367. * @param string $dstPath 上传的文件路径
  368. * @param string $bizAttr 文件属性
  369. * @param string $sliceSize 分片大小
  370. * @param int $insertOnly 是否覆盖同名文件:0 覆盖,1:不覆盖
  371. * @return [type] [description]
  372. */
  373. private function uploadBySlicing(
  374. $bucket, $srcFpath, $dstFpath, $bizAttr=null, $sliceSize=null, $insertOnly=null) {
  375. $srcFpath = realpath($srcFpath);
  376. $fileSize = filesize($srcFpath);
  377. $dstFpath = $this->cosUrlEncode($dstFpath);
  378. $url = $this->generateResUrl($bucket, $dstFpath);
  379. $sliceCount = ceil($fileSize / $sliceSize);
  380. // expiration seconds for one slice mutiply by slice count
  381. // will be the expired seconds for whole file
  382. $expiration = time() + (self::EXPIRED_SECONDS * $sliceCount);
  383. if ($expiration >= (time() + 10 * 24 * 60 * 60)) {
  384. $expiration = time() + 10 * 24 * 60 * 60;
  385. }
  386. $signature = $this->auth->createReusableSignature($expiration, $bucket);
  387. $sliceUploading = new SliceUploading($this->timeout * 1000, self::MAX_RETRY_TIMES);
  388. for ($tryCount = 0; $tryCount < self::MAX_RETRY_TIMES; ++$tryCount) {
  389. if ($sliceUploading->initUploading(
  390. $signature,
  391. $srcFpath,
  392. $url,
  393. $fileSize, $sliceSize, $bizAttr, $insertOnly)) {
  394. break;
  395. }
  396. $errorCode = $sliceUploading->getLastErrorCode();
  397. if ($errorCode === -4019) {
  398. // Delete broken file and retry again on _ERROR_FILE_NOT_FINISH_UPLOAD error.
  399. Cosapi::delFile($bucket, $dstFpath);
  400. continue;
  401. }
  402. if ($tryCount === self::MAX_RETRY_TIMES - 1) {
  403. return array(
  404. 'code' => $sliceUploading->getLastErrorCode(),
  405. 'message' => $sliceUploading->getLastErrorMessage(),
  406. 'request_id' => $sliceUploading->getRequestId(),
  407. );
  408. }
  409. }
  410. if (!$sliceUploading->performUploading()) {
  411. return array(
  412. 'code' => $sliceUploading->getLastErrorCode(),
  413. 'message' => $sliceUploading->getLastErrorMessage(),
  414. 'request_id' => $sliceUploading->getRequestId(),
  415. );
  416. }
  417. if (!$sliceUploading->finishUploading()) {
  418. return array(
  419. 'code' => $sliceUploading->getLastErrorCode(),
  420. 'message' => $sliceUploading->getLastErrorMessage(),
  421. 'request_id' => $sliceUploading->getRequestId(),
  422. );
  423. }
  424. return array(
  425. 'code' => 0,
  426. 'message' => 'SUCCESS',
  427. 'request_id' => $sliceUploading->getRequestId(),
  428. 'data' => array(
  429. 'access_url' => $sliceUploading->getAccessUrl(),
  430. 'resource_path' => $sliceUploading->getResourcePath(),
  431. 'source_url' => $sliceUploading->getSourceUrl(),
  432. ),
  433. );
  434. }
  435. /*
  436. * 内部公共函数
  437. * @param string $bucket bucket名称
  438. * @param string $path 文件夹路径
  439. * @param int $num 拉取的总数
  440. * @param string $context 在翻页查询时候用到
  441. */
  442. private function listBase(
  443. $bucket, $path, $num = 20, $context = null) {
  444. $path = $this->cosUrlEncode($path);
  445. $expired = time() + self::EXPIRED_SECONDS;
  446. $url = $this->generateResUrl($bucket, $path);
  447. $signature = $this->auth->createReusableSignature($expired, $bucket);
  448. $data = array(
  449. 'op' => 'list',
  450. );
  451. if ($num < 0 || $num > 199) {
  452. return array(
  453. 'code' => self::COSAPI_PARAMS_ERROR,
  454. 'message' => 'parameter num invalid, num need less then 200',
  455. );
  456. }
  457. $data['num'] = $num;
  458. if (isset($context)) {
  459. $data['context'] = $context;
  460. }
  461. $url = $url . '?' . http_build_query($data);
  462. $req = array(
  463. 'url' => $url,
  464. 'method' => 'get',
  465. 'timeout' => $this->timeout,
  466. 'header' => array(
  467. 'Authorization: ' . $signature,
  468. ),
  469. );
  470. return $this->sendRequest($req);
  471. }
  472. /*
  473. * 内部公共方法(更新文件和更新文件夹)
  474. * @param string $bucket bucket名称
  475. * @param string $path 路径
  476. * @param string $bizAttr 文件/目录属性
  477. * @param string $authority: eInvalid/eWRPrivate(私有)/eWPrivateRPublic(公有读写)
  478. * @param array $customer_headers_array 携带的用户自定义头域,包括
  479. * 'Cache-Control' => '*'
  480. * 'Content-Type' => '*'
  481. * 'Content-Disposition' => '*'
  482. * 'Content-Language' => '*'
  483. * 'x-cos-meta-自定义内容' => '*'
  484. */
  485. private function updateBase(
  486. $bucket, $path, $bizAttr = null, $authority = null, $custom_headers_array = null) {
  487. $signature = $this->auth->createNonreusableSignature($bucket, $path);
  488. $path = $this->cosUrlEncode($path);
  489. $expired = time() + self::EXPIRED_SECONDS;
  490. $url = $this->generateResUrl($bucket, $path);
  491. $data = array('op' => 'update');
  492. if (isset($bizAttr)) {
  493. $data['biz_attr'] = $bizAttr;
  494. }
  495. if (isset($authority) && strlen($authority) > 0) {
  496. if($this->isAuthorityValid($authority) == false) {
  497. return array(
  498. 'code' => self::COSAPI_PARAMS_ERROR,
  499. 'message' => 'parameter authority invalid');
  500. }
  501. $data['authority'] = $authority;
  502. }
  503. if (isset($custom_headers_array)) {
  504. $data['custom_headers'] = array();
  505. $this->add_customer_header($data['custom_headers'], $custom_headers_array);
  506. }
  507. $data = json_encode($data);
  508. $req = array(
  509. 'url' => $url,
  510. 'method' => 'post',
  511. 'timeout' => $this->timeout,
  512. 'data' => $data,
  513. 'header' => array(
  514. 'Authorization: ' . $signature,
  515. 'Content-Type: application/json',
  516. ),
  517. );
  518. return $this->sendRequest($req);
  519. }
  520. /*
  521. * 内部方法
  522. * @param string $bucket bucket名称
  523. * @param string $path 文件/目录路径
  524. */
  525. private function statBase($bucket, $path) {
  526. $path = $this->cosUrlEncode($path);
  527. $expired = time() + self::EXPIRED_SECONDS;
  528. $url = $this->generateResUrl($bucket, $path);
  529. $signature = $this->auth->createReusableSignature($expired, $bucket);
  530. $data = array('op' => 'stat');
  531. $url = $url . '?' . http_build_query($data);
  532. $req = array(
  533. 'url' => $url,
  534. 'method' => 'get',
  535. 'timeout' => $this->timeout,
  536. 'header' => array(
  537. 'Authorization: ' . $signature,
  538. ),
  539. );
  540. return $this->sendRequest($req);
  541. }
  542. /*
  543. * 内部私有方法
  544. * @param string $bucket bucket名称
  545. * @param string $path 文件/目录路径路径
  546. */
  547. private function delBase($bucket, $path) {
  548. if ($path == "/") {
  549. return array(
  550. 'code' => self::COSAPI_PARAMS_ERROR,
  551. 'message' => 'can not delete bucket using api! go to ' .
  552. 'http://console.qcloud.com/cos to operate bucket');
  553. }
  554. $signature = $this->auth->createNonreusableSignature($bucket, $path);
  555. $path = $this->cosUrlEncode($path);
  556. $expired = time() + self::EXPIRED_SECONDS;
  557. $url = $this->generateResUrl($bucket, $path);
  558. $data = array('op' => 'delete');
  559. $data = json_encode($data);
  560. $req = array(
  561. 'url' => $url,
  562. 'method' => 'post',
  563. 'timeout' => $this->timeout,
  564. 'data' => $data,
  565. 'header' => array(
  566. 'Authorization: ' . $signature,
  567. 'Content-Type: application/json',
  568. ),
  569. );
  570. return $this->sendRequest($req);
  571. }
  572. /*
  573. * 内部公共方法, 路径编码
  574. * @param string $path 待编码路径
  575. */
  576. private function cosUrlEncode($path) {
  577. return str_replace('%2F', '/', rawurlencode($path));
  578. }
  579. /*
  580. * 内部公共方法, 构造URL
  581. * @param string $bucket
  582. * @param string $dstPath
  583. */
  584. private function generateResUrl($bucket, $dstPath) {
  585. $endPoint = str_replace('region', $this->region, $this->endPoint);
  586. return $endPoint . $this->config['app_id'] . '/' . $bucket . $dstPath;
  587. }
  588. /*
  589. * 内部公共方法, 发送消息
  590. * @param string $req
  591. */
  592. private function sendRequest($req) {
  593. $rsp = $this->httpClient->sendRequest($req);
  594. if ($rsp === false) {
  595. return array(
  596. 'code' => self::COSAPI_NETWORK_ERROR,
  597. 'message' => 'network error',
  598. );
  599. }
  600. $info = $this->httpClient->info();
  601. $ret = json_decode($rsp, true);
  602. if ($ret === NULL) {
  603. return array(
  604. 'code' => self::COSAPI_NETWORK_ERROR,
  605. 'message' => $rsp,
  606. 'data' => array()
  607. );
  608. }
  609. return $ret;
  610. }
  611. /**
  612. * Get slice size.
  613. */
  614. private function getSliceSize($sliceSize) {
  615. // Fix slice size to 1MB.
  616. return self::SLICE_SIZE_1M;
  617. }
  618. /*
  619. * 内部方法, 规整文件路径
  620. * @param string $path 文件路径
  621. * @param string $isfolder 是否为文件夹
  622. */
  623. private function normalizerPath($path, $isfolder = False) {
  624. if (preg_match('/^\//', $path) == 0) {
  625. $path = '/' . $path;
  626. }
  627. if ($isfolder == True) {
  628. if (preg_match('/\/$/', $path) == 0) {
  629. $path = $path . '/';
  630. }
  631. }
  632. // Remove unnecessary slashes.
  633. $path = preg_replace('#/+#', '/', $path);
  634. return $path;
  635. }
  636. /**
  637. * 判断authority值是否正确
  638. * @param string $authority
  639. * @return [type] bool
  640. */
  641. private function isAuthorityValid($authority) {
  642. if ($authority == 'eInvalid' || $authority == 'eWRPrivate' || $authority == 'eWPrivateRPublic') {
  643. return true;
  644. }
  645. return false;
  646. }
  647. /**
  648. * 判断是否符合自定义属性
  649. * @param string $key
  650. * @return [type] bool
  651. */
  652. private function isCustomer_header($key) {
  653. if ($key == 'Cache-Control' || $key == 'Content-Type' ||
  654. $key == 'Content-Disposition' || $key == 'Content-Language' ||
  655. $key == 'Content-Encoding' ||
  656. substr($key,0,strlen('x-cos-meta-')) == 'x-cos-meta-') {
  657. return true;
  658. }
  659. return false;
  660. }
  661. /**
  662. * 增加自定义属性到data中
  663. * @param array $data
  664. * @param array $customer_headers_array
  665. * @return [type] void
  666. */
  667. private function add_customer_header(&$data, &$customer_headers_array) {
  668. if (count($customer_headers_array) < 1) {
  669. return;
  670. }
  671. foreach($customer_headers_array as $key=>$value) {
  672. if($this->isCustomer_header($key)) {
  673. $data[$key] = $value;
  674. }
  675. }
  676. }
  677. // Check |$path| is a valid file path.
  678. // Return true on success, otherwise return false.
  679. private function isValidPath($path) {
  680. if (strpos($path, '?') !== false) {
  681. return false;
  682. }
  683. if (strpos($path, '*') !== false) {
  684. return false;
  685. }
  686. if (strpos($path, ':') !== false) {
  687. return false;
  688. }
  689. if (strpos($path, '|') !== false) {
  690. return false;
  691. }
  692. if (strpos($path, '\\') !== false) {
  693. return false;
  694. }
  695. if (strpos($path, '<') !== false) {
  696. return false;
  697. }
  698. if (strpos($path, '>') !== false) {
  699. return false;
  700. }
  701. if (strpos($path, '"') !== false) {
  702. return false;
  703. }
  704. return true;
  705. }
  706. /**
  707. * Copy a file.
  708. * @param $bucket bucket name.
  709. * @param $srcFpath source file path.
  710. * @param $dstFpath destination file path.
  711. * @param $overwrite if the destination location is occupied, overwrite it or not?
  712. * @return array|mixed.
  713. */
  714. public function copyFile($bucket, $srcFpath, $dstFpath, $overwrite = false) {
  715. $srcFpath = $this->normalizerPath($srcFpath, false);
  716. $srcFpath = $this->cosUrlEncode($srcFpath);
  717. $url = $this->generateResUrl($bucket, $srcFpath);
  718. $sign = $this->auth->createNonreusableSignature($bucket, $srcFpath);
  719. $data = array(
  720. 'op' => 'copy',
  721. 'dest_fileid' => $dstFpath,
  722. 'to_over_write' => $overwrite ? 1 : 0,
  723. );
  724. $req = array(
  725. 'url' => $url,
  726. 'method' => 'post',
  727. 'timeout' => $this->timeout,
  728. 'data' => $data,
  729. 'header' => array(
  730. 'Authorization: ' . $sign,
  731. ),
  732. );
  733. return $this->sendRequest($req);
  734. }
  735. /**
  736. * Move a file.
  737. * @param $bucket bucket name.
  738. * @param $srcFpath source file path.
  739. * @param $dstFpath destination file path.
  740. * @param $overwrite if the destination location is occupied, overwrite it or not?
  741. * @return array|mixed.
  742. */
  743. public function moveFile($bucket, $srcFpath, $dstFpath, $overwrite = false) {
  744. $srcFpath = $this->normalizerPath($srcFpath, false);
  745. $srcFpath = $this->cosUrlEncode($srcFpath);
  746. $url = $this->generateResUrl($bucket, $srcFpath);
  747. $sign = $this->auth->createNonreusableSignature($bucket, $srcFpath);
  748. $data = array(
  749. 'op' => 'move',
  750. 'dest_fileid' => $dstFpath,
  751. 'to_over_write' => $overwrite ? 1 : 0,
  752. );
  753. $req = array(
  754. 'url' => $url,
  755. 'method' => 'post',
  756. 'timeout' => $this->timeout,
  757. 'data' => $data,
  758. 'header' => array(
  759. 'Authorization: ' . $sign,
  760. ),
  761. );
  762. return $this->sendRequest($req);
  763. }
  764. /**
  765. * Get file's url for downloading.
  766. * @param $bucket bucket name.
  767. * @param $fpath file path.
  768. * @param $expireAfterSecs url will expire after this secconds.
  769. * @return array|mixed.
  770. */
  771. public function getDownloadUrl($bucket, $fpath, $expireAfterSecs) {
  772. $fpath = $this->normalizerPath($fpath, false);
  773. $expiration = time() + $expireAfterSecs;
  774. $signature = $this->auth->createReusableSignature($expiration, $bucket);
  775. $appId = $this->config['app_id'];
  776. $region = $this->config['region'];
  777. $accessUrl = "http://$bucket-$appId.file.myqcloud.com$fpath?sign=$signature";
  778. $sourceUrl = "http://$bucket-$appId.cos${region}.myqcloud.com$fpath?sign=$signature";
  779. $url = "http://$region.file.myqcloud.com/files/v2/${appId}${fpath}?sign=$signature";
  780. return array(
  781. 'code' => 0,
  782. 'message' => 'SUCCESS',
  783. 'data' => array(
  784. 'access_url' => $accessUrl,
  785. 'source_url' => $sourceUrl,
  786. 'url' => $url,
  787. ),
  788. );
  789. }
  790. }