You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1317 lines
48 KiB

3 years ago
  1. <?php
  2. namespace Think\Upload\Driver\Bcs;
  3. use Think\Upload\Driver\Bcs\BCS_MimeTypes;
  4. use Think\Upload\Driver\Bcs\BCS_RequestCore;
  5. use Think\Upload\Driver\Bcs\BCS_ResponseCore;
  6. if (! defined ( 'BCS_API_PATH' )) {
  7. define ( 'BCS_API_PATH', dirname ( __FILE__ ) );
  8. }
  9. //AK 公钥
  10. define ( 'BCS_AK', '' );
  11. //SK 私钥
  12. define ( 'BCS_SK', '' );
  13. //superfile 每个object分片后缀
  14. define ( 'BCS_SUPERFILE_POSTFIX', '_bcs_superfile_' );
  15. //sdk superfile分片大小 ,单位 B(字节)
  16. define ( 'BCS_SUPERFILE_SLICE_SIZE', 1024 * 1024 );
  17. require_once (BCS_API_PATH . '/requestcore.class.php');
  18. require_once (BCS_API_PATH . '/mimetypes.class.php');
  19. /**
  20. * Default BCS Exception.
  21. */
  22. class BCS_Exception extends \Exception {
  23. }
  24. /**
  25. * BCS API
  26. */
  27. class BaiduBCS {
  28. /*%******************************************************************************************%*/
  29. // CLASS CONSTANTS
  30. //百度云存储默认外网域名
  31. const DEFAULT_URL = 'bcs.duapp.com';
  32. //SDK 版本
  33. const API_VERSION = '2012-4-17-1.0.1.6';
  34. const ACL = 'acl';
  35. const BUCKET = 'bucket';
  36. const OBJECT = 'object';
  37. const HEADERS = 'headers';
  38. const METHOD = 'method';
  39. const AK = 'ak';
  40. const SK = 'sk';
  41. const QUERY_STRING = "query_string";
  42. const IMPORT_BCS_LOG_METHOD = "import_bs_log_method";
  43. const IMPORT_BCS_PRE_FILTER = "import_bs_pre_filter";
  44. const IMPORT_BCS_POST_FILTER = "import_bs_post_filter";
  45. /**********************************************************
  46. ******************* Policy Constants**********************
  47. **********************************************************/
  48. const STATEMETS = 'statements';
  49. //Action 用户动作
  50. //'*'代表所有action
  51. const BCS_SDK_ACL_ACTION_ALL = '*';
  52. //与bucket相关的action
  53. const BCS_SDK_ACL_ACTION_LIST_OBJECT = 'list_object';
  54. const BCS_SDK_ACL_ACTION_PUT_BUCKET_POLICY = 'put_bucket_policy';
  55. const BCS_SDK_ACL_ACTION_GET_BUCKET_POLICY = 'get_bucket_policy';
  56. const BCS_SDK_ACL_ACTION_DELETE_BUCKET = 'delete_bucket';
  57. //与object相关的action
  58. const BCS_SDK_ACL_ACTION_GET_OBJECT = 'get_object';
  59. const BCS_SDK_ACL_ACTION_PUT_OBJECT = 'put_object';
  60. const BCS_SDK_ACL_ACTION_DELETE_OBJECT = 'delete_object';
  61. const BCS_SDK_ACL_ACTION_PUT_OBJECT_POLICY = 'put_object_policy';
  62. const BCS_SDK_ACL_ACTION_GET_OBJECT_POLICY = 'get_object_policy';
  63. static $ACL_ACTIONS = array (
  64. self::BCS_SDK_ACL_ACTION_ALL,
  65. self::BCS_SDK_ACL_ACTION_LIST_OBJECT,
  66. self::BCS_SDK_ACL_ACTION_PUT_BUCKET_POLICY,
  67. self::BCS_SDK_ACL_ACTION_GET_BUCKET_POLICY,
  68. self::BCS_SDK_ACL_ACTION_DELETE_BUCKET,
  69. self::BCS_SDK_ACL_ACTION_GET_OBJECT,
  70. self::BCS_SDK_ACL_ACTION_PUT_OBJECT,
  71. self::BCS_SDK_ACL_ACTION_DELETE_OBJECT,
  72. self::BCS_SDK_ACL_ACTION_PUT_OBJECT_POLICY,
  73. self::BCS_SDK_ACL_ACTION_GET_OBJECT_POLICY );
  74. //EFFECT:
  75. const BCS_SDK_ACL_EFFECT_ALLOW = "allow";
  76. const BCS_SDK_ACL_EFFECT_DENY = "deny";
  77. static $ACL_EFFECTS = array (
  78. self::BCS_SDK_ACL_EFFECT_ALLOW,
  79. self::BCS_SDK_ACL_EFFECT_DENY );
  80. //ACL_TYPE:
  81. //公开读权限
  82. const BCS_SDK_ACL_TYPE_PUBLIC_READ = "public-read";
  83. //公开写权限(不具备删除权限)
  84. const BCS_SDK_ACL_TYPE_PUBLIC_WRITE = "public-write";
  85. //公开读写权限(不具备删除权限)
  86. const BCS_SDK_ACL_TYPE_PUBLIC_READ_WRITE = "public-read-write";
  87. //公开所有权限
  88. const BCS_SDK_ACL_TYPE_PUBLIC_CONTROL = "public-control";
  89. //私有权限,仅bucket所有者具有所有权限
  90. const BCS_SDK_ACL_TYPE_PRIVATE = "private";
  91. //SDK中开放此上五种acl_tpe
  92. static $ACL_TYPES = array (
  93. self::BCS_SDK_ACL_TYPE_PUBLIC_READ,
  94. self::BCS_SDK_ACL_TYPE_PUBLIC_WRITE,
  95. self::BCS_SDK_ACL_TYPE_PUBLIC_READ_WRITE,
  96. self::BCS_SDK_ACL_TYPE_PUBLIC_CONTROL,
  97. self::BCS_SDK_ACL_TYPE_PRIVATE );
  98. /*%******************************************************************************************%*/
  99. // PROPERTIES
  100. //是否使用ssl
  101. protected $use_ssl = false;
  102. //公钥 account key
  103. private $ak;
  104. //私钥 secret key
  105. private $sk;
  106. //云存储server地址
  107. private $hostname;
  108. /**
  109. * 构造函数
  110. * @param string $ak 云存储公钥
  111. * @param string $sk 云存储私钥
  112. * @param string $hostname 云存储Api访问地址
  113. * @throws BCS_Exception
  114. */
  115. public function __construct($ak = NULL, $sk = NULL, $hostname = NULL) {
  116. //valid ak & sk
  117. if (! $ak && ! defined ( 'BCS_AK' ) && false === getenv ( 'HTTP_BAE_ENV_AK' )) {
  118. throw new BCS_Exception ( 'No account key was passed into the constructor.' );
  119. }
  120. if (! $sk && ! defined ( 'BCS_SK' ) && false === getenv ( 'HTTP_BAE_ENV_SK' )) {
  121. throw new BCS_Exception ( 'No secret key was passed into the constructor.' );
  122. }
  123. if ($ak && $sk) {
  124. $this->ak = $ak;
  125. $this->sk = $sk;
  126. } elseif (defined ( 'BCS_AK' ) && defined ( 'BCS_SK' ) && strlen ( BCS_AK ) > 0 && strlen ( BCS_SK ) > 0) {
  127. $this->ak = BCS_AK;
  128. $this->sk = BCS_SK;
  129. } elseif (false !== getenv ( 'HTTP_BAE_ENV_AK' ) && false !== getenv ( 'HTTP_BAE_ENV_SK' )) {
  130. $this->ak = getenv ( 'HTTP_BAE_ENV_AK' );
  131. $this->sk = getenv ( 'HTTP_BAE_ENV_SK' );
  132. } else {
  133. throw new BCS_Exception ( 'Construct can not get ak &sk pair, please check!' );
  134. }
  135. //valid $hostname
  136. if (NULL !== $hostname) {
  137. $this->hostname = $hostname;
  138. } elseif (false !== getenv ( 'HTTP_BAE_ENV_ADDR_BCS' )) {
  139. $this->hostname = getenv ( 'HTTP_BAE_ENV_ADDR_BCS' );
  140. } else {
  141. $this->hostname = self::DEFAULT_URL;
  142. }
  143. }
  144. /**
  145. * 将消息发往Baidu BCS.
  146. * @param array $opt
  147. * @return BCS_ResponseCore
  148. */
  149. private function authenticate($opt) {
  150. //set common param into opt
  151. $opt [self::AK] = $this->ak;
  152. $opt [self::SK] = $this->sk;
  153. // Validate the S3 bucket name, only list_bucket didnot need validate_bucket
  154. if (! ('/' == $opt [self::OBJECT] && '' == $opt [self::BUCKET] && 'GET' == $opt [self::METHOD] && ! isset ( $opt [self::QUERY_STRING] [self::ACL] )) && ! self::validate_bucket ( $opt [self::BUCKET] )) {
  155. throw new BCS_Exception ( $opt [self::BUCKET] . 'is not valid, please check!' );
  156. }
  157. //Validate object
  158. if (isset ( $opt [self::OBJECT] ) && ! self::validate_object ( $opt [self::OBJECT] )) {
  159. throw new BCS_Exception ( "Invalid object param[" . $opt [self::OBJECT] . "], please check.", - 1 );
  160. }
  161. //construct url
  162. $url = $this->format_url ( $opt );
  163. if ($url === false) {
  164. throw new BCS_Exception ( 'Can not format url, please check your param!', - 1 );
  165. }
  166. $opt ['url'] = $url;
  167. $this->log ( "[method:" . $opt [self::METHOD] . "][url:$url]", $opt );
  168. //build request
  169. $request = new BCS_RequestCore ( $opt ['url'] );
  170. $headers = array (
  171. 'Content-Type' => 'application/x-www-form-urlencoded' );
  172. $request->set_method ( $opt [self::METHOD] );
  173. //Write get_object content to fileWriteTo
  174. if (isset ( $opt ['fileWriteTo'] )) {
  175. $request->set_write_file ( $opt ['fileWriteTo'] );
  176. }
  177. // Merge the HTTP headers
  178. if (isset ( $opt [self::HEADERS] )) {
  179. $headers = array_merge ( $headers, $opt [self::HEADERS] );
  180. }
  181. // Set content to Http-Body
  182. if (isset ( $opt ['content'] )) {
  183. $request->set_body ( $opt ['content'] );
  184. }
  185. // Upload file
  186. if (isset ( $opt ['fileUpload'] )) {
  187. if (! file_exists ( $opt ['fileUpload'] )) {
  188. throw new BCS_Exception ( 'File[' . $opt ['fileUpload'] . '] not found!', - 1 );
  189. }
  190. $request->set_read_file ( $opt ['fileUpload'] );
  191. // Determine the length to read from the file
  192. $length = $request->read_stream_size; // The file size by default
  193. $file_size = $length;
  194. if (isset ( $opt ["length"] )) {
  195. if ($opt ["length"] > $file_size) {
  196. throw new BCS_Exception ( "Input opt[length] invalid! It can not bigger than file-size", - 1 );
  197. }
  198. $length = $opt ['length'];
  199. }
  200. if (isset ( $opt ['seekTo'] ) && ! isset ( $opt ["length"] )) {
  201. // Read from seekTo until EOF by default, when set seekTo but not set $opt["length"]
  202. $length -= ( integer ) $opt ['seekTo'];
  203. }
  204. $request->set_read_stream_size ( $length );
  205. // Attempt to guess the correct mime-type
  206. if ($headers ['Content-Type'] === 'application/x-www-form-urlencoded') {
  207. $extension = explode ( '.', $opt ['fileUpload'] );
  208. $extension = array_pop ( $extension );
  209. $mime_type = BCS_MimeTypes::get_mimetype ( $extension );
  210. $headers ['Content-Type'] = $mime_type;
  211. }
  212. $headers ['Content-MD5'] = '';
  213. }
  214. // Handle streaming file offsets
  215. if (isset ( $opt ['seekTo'] )) {
  216. // Pass the seek position to BCS_RequestCore
  217. $request->set_seek_position ( ( integer ) $opt ['seekTo'] );
  218. }
  219. // Add headers to request and compute the string to sign
  220. foreach ( $headers as $header_key => $header_value ) {
  221. // Strip linebreaks from header values as they're illegal and can allow for security issues
  222. $header_value = str_replace ( array (
  223. "\r",
  224. "\n" ), '', $header_value );
  225. // Add the header if it has a value
  226. if ($header_value !== '') {
  227. $request->add_header ( $header_key, $header_value );
  228. }
  229. }
  230. // Set the curl options.
  231. if (isset ( $opt ['curlopts'] ) && count ( $opt ['curlopts'] )) {
  232. $request->set_curlopts ( $opt ['curlopts'] );
  233. }
  234. $request->send_request ();
  235. require_once(dirname(__FILE__). "/requestcore.class.php");
  236. return new BCS_ResponseCore ( $request->get_response_header (), $request->get_response_body (), $request->get_response_code () );
  237. }
  238. /**
  239. * 获取当前密钥对拥有者的bucket列表
  240. * @param array $opt (Optional)
  241. * BaiduBCS::IMPORT_BCS_LOG_METHOD - String - Optional: 支持用户传入日志处理函数,函数定义如 function f($log)
  242. * @throws BCS_Exception
  243. * @return BCS_ResponseCore
  244. */
  245. public function list_bucket($opt = array()) {
  246. $this->assertParameterArray ( $opt );
  247. $opt [self::BUCKET] = '';
  248. $opt [self::METHOD] = 'GET';
  249. $opt [self::OBJECT] = '/';
  250. $response = $this->authenticate ( $opt );
  251. $this->log ( $response->isOK () ? "List bucket success!" : "List bucket failed! Response: [" . $response->body . "]", $opt );
  252. return $response;
  253. }
  254. /**
  255. * 创建 bucket
  256. * @param string $bucket (Required) bucket名称
  257. * @param string $acl (Optional) bucket权限设置,若为null,使用server分配的默认权限
  258. * @param array $opt (Optional)
  259. * @throws BCS_Exception
  260. * @return BCS_ResponseCore
  261. */
  262. public function create_bucket($bucket, $acl = NULL, $opt = array()) {
  263. $this->assertParameterArray ( $opt );
  264. $opt [self::BUCKET] = $bucket;
  265. $opt [self::METHOD] = 'PUT';
  266. $opt [self::OBJECT] = '/';
  267. if (NULL !== $acl) {
  268. if (! in_array ( $acl, self::$ACL_TYPES )) {
  269. throw new BCS_Exception ( "Invalid acl_type[" . $acl . "], please check!", - 1 );
  270. }
  271. self::set_header_into_opt ( "x-bs-acl", $acl, $opt );
  272. }
  273. $response = $this->authenticate ( $opt );
  274. $this->log ( $response->isOK () ? "Create bucket success!" : "Create bucket failed! Response: [" . $response->body . "]", $opt );
  275. return $response;
  276. }
  277. /**
  278. * 删除bucket
  279. * @param string $bucket (Required)
  280. * @param array $opt (Optional)
  281. * @return boolean|BCS_ResponseCore
  282. */
  283. public function delete_bucket($bucket, $opt = array()) {
  284. $this->assertParameterArray ( $opt );
  285. $opt [self::BUCKET] = $bucket;
  286. $opt [self::METHOD] = 'DELETE';
  287. $opt [self::OBJECT] = '/';
  288. $response = $this->authenticate ( $opt );
  289. $this->log ( $response->isOK () ? "Delete bucket success!" : "Delete bucket failed! Response: [" . $response->body . "]", $opt );
  290. return $response;
  291. }
  292. /**
  293. * 设置bucket的acl,有三种模式,
  294. * (1).设置详细json格式的acl;
  295. * a. $acl 为json的array
  296. * b. $acl 为json的string
  297. * (2).通过acl_type字段进行设置
  298. * a. $acl 为BaiduBCS::$ACL_TYPES中的字段
  299. * @param string $bucket (Required)
  300. * @param string $acl (Required)
  301. * @param array $opt (Optional)
  302. * @return boolean|BCS_ResponseCore
  303. */
  304. public function set_bucket_acl($bucket, $acl, $opt = array()) {
  305. $this->assertParameterArray ( $opt );
  306. $result = $this->analyze_user_acl ( $acl );
  307. $opt = array_merge ( $opt, $result );
  308. $opt [self::BUCKET] = $bucket;
  309. $opt [self::METHOD] = 'PUT';
  310. $opt [self::OBJECT] = '/';
  311. $opt [self::QUERY_STRING] = array (
  312. self::ACL => 1 );
  313. $response = $this->authenticate ( $opt );
  314. $this->log ( $response->isOK () ? "Set bucket acl success!" : "Set bucket acl failed! Response: [" . $response->body . "]", $opt );
  315. return $response;
  316. }
  317. /**
  318. * 获取bucket的acl
  319. * @param string $bucket (Required)
  320. * @param array $opt (Optional)
  321. * @return BCS_ResponseCore
  322. */
  323. public function get_bucket_acl($bucket, $opt = array()) {
  324. $this->assertParameterArray ( $opt );
  325. $opt [self::BUCKET] = $bucket;
  326. $opt [self::METHOD] = 'GET';
  327. $opt [self::OBJECT] = '/';
  328. $opt [self::QUERY_STRING] = array (
  329. self::ACL => 1 );
  330. $response = $this->authenticate ( $opt );
  331. $this->log ( $response->isOK () ? "Get bucket acl success!" : "Get bucket acl failed! Response: [" . $response->body . "]", $opt );
  332. return $response;
  333. }
  334. /**
  335. * 获取bucket中object列表
  336. * @param string $bucket (Required)
  337. * @param array $opt (Optional)
  338. * start : 主要用于翻页功能,用法同mysql中start的用法
  339. * limit : 主要用于翻页功能,用法同mysql中limit的用法
  340. * prefix: 只返回以prefix为前缀的object,此处prefix必须以'/'开头
  341. * @throws BCS_Exception
  342. * @return BCS_ResponseCore
  343. */
  344. public function list_object($bucket, $opt = array()) {
  345. $this->assertParameterArray ( $opt );
  346. $opt [self::BUCKET] = $bucket;
  347. if (empty ( $opt [self::BUCKET] )) {
  348. throw new BCS_Exception ( "Bucket should not be empty, please check", - 1 );
  349. }
  350. $opt [self::METHOD] = 'GET';
  351. $opt [self::OBJECT] = '/';
  352. $opt [self::QUERY_STRING] = array ();
  353. if (isset ( $opt ['start'] ) && is_int ( $opt ['start'] )) {
  354. $opt [self::QUERY_STRING] ['start'] = $opt ['start'];
  355. }
  356. if (isset ( $opt ['limit'] ) && is_int ( $opt ['limit'] )) {
  357. $opt [self::QUERY_STRING] ['limit'] = $opt ['limit'];
  358. }
  359. if (isset ( $opt ['prefix'] )) {
  360. $opt [self::QUERY_STRING] ['prefix'] = rawurlencode ( $opt ['prefix'] );
  361. }
  362. $response = $this->authenticate ( $opt );
  363. $this->log ( $response->isOK () ? "List object success!" : "Lit object failed! Response: [" . $response->body . "]", $opt );
  364. return $response;
  365. }
  366. /**
  367. * 以目录形式获取bucket中object列表
  368. * @param string $bucket (Required)
  369. * @param $dir (Required)
  370. * 目录名,格式为必须以'/'开头和结尾,默认为'/'
  371. * @param string $list_model (Required)
  372. * 目录展现形式,值可以为0,1,2,默认为2,以下对各个值的功能进行介绍:
  373. * 0->只返回object列表,不返回子目录列表
  374. * 1->只返回子目录列表,不返回object列表
  375. * 2->同时返回子目录列表和object列表
  376. * @param array $opt (Optional)
  377. * start : 主要用于翻页功能,用法同mysql中start的用法
  378. * limit : 主要用于翻页功能,用法同mysql中limit的用法
  379. * @throws BCS_Exception
  380. * @return BCS_ResponseCore
  381. */
  382. public function list_object_by_dir($bucket, $dir = '/', $list_model = 2, $opt = array()) {
  383. $this->assertParameterArray ( $opt );
  384. $opt [self::BUCKET] = $bucket;
  385. if (empty ( $opt [self::BUCKET] )) {
  386. throw new BCS_Exception ( "Bucket should not be empty, please check", - 1 );
  387. }
  388. $opt [self::METHOD] = 'GET';
  389. $opt [self::OBJECT] = '/';
  390. $opt [self::QUERY_STRING] = array ();
  391. if (isset ( $opt ['start'] ) && is_int ( $opt ['start'] )) {
  392. $opt [self::QUERY_STRING] ['start'] = $opt ['start'];
  393. }
  394. if (isset ( $opt ['limit'] ) && is_int ( $opt ['limit'] )) {
  395. $opt [self::QUERY_STRING] ['limit'] = $opt ['limit'];
  396. }
  397. $opt [self::QUERY_STRING] ['prefix'] = rawurlencode ( $dir );
  398. $opt [self::QUERY_STRING] ['dir'] = $list_model;
  399. $response = $this->authenticate ( $opt );
  400. $this->log ( $response->isOK () ? "List object success!" : "Lit object failed! Response: [" . $response->body . "]", $opt );
  401. return $response;
  402. }
  403. /**
  404. * 上传文件
  405. * @param string $bucket (Required)
  406. * @param string $object (Required)
  407. * @param string $file (Required); 需要上传的文件的文件路径
  408. * @param array $opt (Optional)
  409. * filename - Optional; 指定文件名
  410. * acl - Optional ; 上传文件的acl,只能使用acl_type
  411. * seekTo - Optional; 上传文件的偏移位置
  412. * length - Optional; 待上传长度
  413. * @return BCS_ResponseCore
  414. */
  415. public function create_object($bucket, $object, $file, $opt = array()) {
  416. $this->assertParameterArray ( $opt );
  417. $opt [self::BUCKET] = $bucket;
  418. $opt [self::OBJECT] = $object;
  419. $opt ['fileUpload'] = $file;
  420. $opt [self::METHOD] = 'PUT';
  421. if (isset ( $opt ['acl'] )) {
  422. if (in_array ( $opt ['acl'], self::$ACL_TYPES )) {
  423. self::set_header_into_opt ( "x-bs-acl", $opt ['acl'], $opt );
  424. } else {
  425. throw new BCS_Exception ( "Invalid acl string, it should be acl_type", - 1 );
  426. }
  427. unset ( $opt ['acl'] );
  428. }
  429. if (isset ( $opt ['filename'] )) {
  430. self::set_header_into_opt ( "Content-Disposition", 'attachment; filename=' . $opt ['filename'], $opt );
  431. }
  432. $response = $this->authenticate ( $opt );
  433. $this->log ( $response->isOK () ? "Create object[$object] file[$file] success!" : "Create object[$object] file[$file] failed! Response: [" . $response->body . "] Logid[" . $response->header ["x-bs-request-id"] . "]", $opt );
  434. return $response;
  435. }
  436. /**
  437. * 上传文件
  438. * @param string $bucket (Required)
  439. * @param string $object (Required)
  440. * @param string $file (Required); 需要上传的文件的文件路径
  441. * @param array $opt (Optional)
  442. * filename - Optional; 指定文件名
  443. * acl - Optional ; 上传文件的acl,只能使用acl_type
  444. * @return BCS_ResponseCore
  445. */
  446. public function create_object_by_content($bucket, $object, $content, $opt = array()) {
  447. $this->assertParameterArray ( $opt );
  448. $opt [self::BUCKET] = $bucket;
  449. $opt [self::OBJECT] = $object;
  450. $opt [self::METHOD] = 'PUT';
  451. if ($content !== NULL && is_string ( $content )) {
  452. $opt ['content'] = $content;
  453. } else {
  454. throw new BCS_Exception ( "Invalid object content, please check.", - 1 );
  455. }
  456. if (isset ( $opt ['acl'] )) {
  457. if (in_array ( $opt ['acl'], self::$ACL_TYPES )) {
  458. self::set_header_into_opt ( "x-bs-acl", $opt ['acl'], $opt );
  459. } else {
  460. throw new BCS_Exception ( "Invalid acl string, it should be acl_type", - 1 );
  461. }
  462. unset ( $opt ['acl'] );
  463. }
  464. if (isset ( $opt ['filename'] )) {
  465. self::set_header_into_opt ( "Content-Disposition", 'attachment; filename=' . $opt ['filename'], $opt );
  466. }
  467. $response = $this->authenticate ( $opt );
  468. $this->log ( $response->isOK () ? "Create object[$object] success!" : "Create object[$object] failed! Response: [" . $response->body . "] Logid[" . $response->header ["x-bs-request-id"] . "]", $opt );
  469. return $response;
  470. }
  471. /**
  472. * 通过superfile的方式上传文件
  473. * @param string $bucket (Required)
  474. * @param string $object (Required)
  475. * @param string $file (Required); 需要上传的文件的文件路径
  476. * @param array $opt (Optional)
  477. * filename - Optional; 指定文件名
  478. * sub_object_size - Optional; 指定子文件的划分大小,单位B,建议以256KB为单位进行子object划分,默认为1MB进行划分
  479. * @return BCS_ResponseCore
  480. */
  481. public function create_object_superfile($bucket, $object, $file, $opt = array()) {
  482. if (isset ( $opt ['length'] ) || isset ( $opt ['seekTo'] )) {
  483. throw new BCS_Exception ( "Temporary unsupport opt of length and seekTo of superfile.", - 1 );
  484. }
  485. //$opt array
  486. $this->assertParameterArray ( $opt );
  487. $opt [self::BUCKET] = $bucket;
  488. $opt ['fileUpload'] = $file;
  489. $opt [self::METHOD] = 'PUT';
  490. if (isset ( $opt ['acl'] )) {
  491. if (in_array ( $opt ['acl'], self::$ACL_TYPES )) {
  492. self::set_header_into_opt ( "x-bs-acl", $opt ['acl'], $opt );
  493. } else {
  494. throw new BCS_Exception ( "Invalid acl string, it should be acl_type", - 1 );
  495. }
  496. unset ( $opt ['acl'] );
  497. }
  498. //切片上传
  499. if (! file_exists ( $opt ['fileUpload'] )) {
  500. throw new BCS_Exception ( 'File not found!', - 1 );
  501. }
  502. $fileSize = filesize ( $opt ['fileUpload'] );
  503. $sub_object_size = 1024 * 1024; //default 1MB
  504. if (defined ( "BCS_SUPERFILE_SLICE_SIZE" )) {
  505. $sub_object_size = BCS_SUPERFILE_SLICE_SIZE;
  506. }
  507. if (isset ( $opt ["sub_object_size"] )) {
  508. if (is_int ( $opt ["sub_object_size"] ) && $opt ["sub_object_size"] > 0) {
  509. $sub_object_size = $opt ["sub_object_size"];
  510. } else {
  511. throw new BCS_Exception ( "Param [sub_object_size] invalid ,please check!", - 1 );
  512. }
  513. }
  514. $sliceNum = intval ( ceil ( $fileSize / $sub_object_size ) );
  515. $this->log ( "File[" . $opt ['fileUpload'] . "], size=[$fileSize], sub_object_size=[$sub_object_size], sub_object_num=[$sliceNum]", $opt );
  516. $object_list = array (
  517. 'object_list' => array () );
  518. for($i = 0; $i < $sliceNum; $i ++) {
  519. //send slice
  520. $opt ['seekTo'] = $i * $sub_object_size;
  521. if (($i + 1) === $sliceNum) {
  522. //last sub object
  523. $opt ['length'] = (0 === $fileSize % $sub_object_size) ? $sub_object_size : $fileSize % $sub_object_size;
  524. } else {
  525. $opt ['length'] = $sub_object_size;
  526. }
  527. $opt [self::OBJECT] = $object . BCS_SUPERFILE_POSTFIX . $i;
  528. $object_list ['object_list'] ['part_' . $i] = array ();
  529. $object_list ['object_list'] ['part_' . $i] ['url'] = 'bs://' . $bucket . $opt [self::OBJECT];
  530. $this->log ( "Begin to upload Sub-object[" . $opt [self::OBJECT] . "][$i/$sliceNum][seekto:" . $opt ['seekTo'] . "][Length:" . $opt ['length'] . "]", $opt );
  531. $response = $this->create_object ( $bucket, $opt [self::OBJECT], $file, $opt );
  532. if ($response->isOK ()) {
  533. $this->log ( "Sub-object upload[" . $opt [self::OBJECT] . "][$i/$sliceNum][seekto:" . $opt ['seekTo'] . "][Length:" . $opt ['length'] . "]success! ", $opt );
  534. $object_list ['object_list'] ['part_' . $i] ['etag'] = $response->header ['Content-MD5'];
  535. continue;
  536. } else {
  537. $this->log ( "Sub-object upload[" . $opt [self::OBJECT] . "][$i/$sliceNum] failed! ", $opt );
  538. return $response;
  539. }
  540. }
  541. //将子文件分片的列表构造成 superfile
  542. unset ( $opt ['fileUpload'] );
  543. unset ( $opt ['length'] );
  544. unset ( $opt ['seekTo'] );
  545. $opt ['content'] = self::array_to_json ( $object_list );
  546. $opt [self::QUERY_STRING] = array (
  547. "superfile" => 1 );
  548. $opt [self::OBJECT] = $object;
  549. if (isset ( $opt ['filename'] )) {
  550. self::set_header_into_opt ( "Content-Disposition", 'attachment; filename=' . $opt ['filename'], $opt );
  551. }
  552. $response = $this->authenticate ( $opt );
  553. $this->log ( $response->isOK () ? "Create object-superfile success!" : "Create object-superfile failed! Response: [" . $response->body . "]", $opt );
  554. return $response;
  555. }
  556. /**
  557. * 将目录中的所有文件进行上传,每个文件为单独object,object命名方式下详:
  558. * 如有 /home/worker/a/b/c.txt 需上传目录为$dir=/home/worker/a
  559. * object命令方式为
  560. * 1. object默认命名方式为 “子目录名 +文件名”,如上述文件c.txt,默认为 '/b/c.txt'
  561. * 2. 增强命名模式,在$opt中有可选参数进行配置
  562. * 举例说明 :prefix . has_sub_directory?"/b":"" . '/c.txt'
  563. * @param string $bucket (Required)
  564. * @param string $dir (Required)
  565. * @param array $opt(Optional)
  566. * string prefix 文件object前缀
  567. * boolean has_sub_directory(default=true) object命名中是否携带文件的子目录结构,若置为false,请确认待上传的目录和所有子目录中没有重名文件,否则会产生object覆盖问题
  568. * BaiduBCS::IMPORT_BCS_PRE_FILTER 用户可自定义上传文件前的操作函数
  569. * 1. 函数参数列表顺序需为 ($bucket,$object,$file,&$opt),注意$opt为upload_directory函数传入的$opt的拷贝,只对当前object生效
  570. * 2. 函数返回值必须为boolean,当true该文件进行上传,若false跳过上传
  571. * 3. 如果函数返回false,将不会进行post_filter的调用
  572. * BaiduBCS::IMPORT_BCS_POST_FILTER 用户可自定义上传文件后的操作函数
  573. * 1. 函数参数列表顺序需为 ($bucket,$object,$file,&$opt,$response),注意$opt为upload_directory函数传入的$opt的拷贝,只对当前object生效
  574. * 2. 函数返回值无要求
  575. * string seek_object 用户断点续传,需要为object名称,如果该object在目录中不存在,抛出异常,若存在则将该object和此后的object进行上传
  576. * string seek_object_id 作用同seek_object,只需要传入上传过程中日志中展示的[a/b]中object序号即可,注意object序号是以1开始计算的
  577. * @return array 数组形式的上传结果
  578. * 'success' => int 上传成功的文件数目
  579. * 'skipped' => int 被跳过的文件
  580. * 'failed' => array() 上传失败的文件
  581. *
  582. */
  583. public function upload_directory($bucket, $dir, $opt = array()) {
  584. $this->assertParameterArray ( $opt );
  585. if (! is_dir ( $dir )) {
  586. throw new BCS_Exception ( "$dir is not a dir!", - 1 );
  587. }
  588. $result = array (
  589. "success" => 0,
  590. "failed" => array (),
  591. "skipped" => 0 );
  592. $prefix = "";
  593. if (isset ( $opt ['prefix'] )) {
  594. $prefix = $opt ['prefix'];
  595. }
  596. $has_sub_directory = true;
  597. if (isset ( $opt ['has_sub_directory'] ) && is_bool ( $opt ['has_sub_directory'] )) {
  598. $has_sub_directory = $opt ['has_sub_directory'];
  599. }
  600. //获取文件树和构造object名
  601. $file_tree = self::get_filetree ( $dir );
  602. $objects = array ();
  603. foreach ( $file_tree as $file ) {
  604. $object = $has_sub_directory == true ? substr ( $file, strlen ( $dir ) ) : "/" . basename ( $file );
  605. $objects [$prefix . $object] = $file;
  606. }
  607. $objectCount = count ( $objects );
  608. $before_upload_log = "Upload directory: bucket[$bucket] upload_dir[$dir] file_sum[$objectCount]";
  609. if (isset ( $opt ["seek_object_id"] )) {
  610. $before_upload_log .= " seek_object_id[" . $opt ["seek_object_id"] . "/$objectCount]";
  611. }
  612. if (isset ( $opt ["seek_object"] )) {
  613. $before_upload_log .= " seek_object[" . $opt ["seek_object"] . "]";
  614. }
  615. $this->log ( $before_upload_log, $opt );
  616. //查看是否需要查询断点,进行断点续传
  617. if (isset ( $opt ["seek_object_id"] ) && isset ( $opt ["seek_object"] )) {
  618. throw new BCS_Exception ( "Can not set see_object_id and seek_object at the same time!", - 1 );
  619. }
  620. $num = 1;
  621. if (isset ( $opt ["seek_object"] )) {
  622. if (isset ( $objects [$opt ["seek_object"]] )) {
  623. foreach ( $objects as $object => $file ) {
  624. if ($object != $opt ["seek_object"]) {
  625. //当非断点文件,该object已完成上传
  626. $this->log ( "Seeking[" . $opt ["seek_object"] . "]. Skip id[$num/$objectCount]object[$object]file[$file].", $opt );
  627. //$result ['skipped'] [] = "[$num/$objectCount] " . $file;
  628. $result ['skipped'] ++;
  629. unset ( $objects [$object] );
  630. } else {
  631. //当找到断点文件,停止循环,从断点文件重新上传
  632. //当非断点文件,该object已完成上传
  633. $this->log ( "Found seek id[$num/$objectCount]object[$object]file[$file], begin from here.", $opt );
  634. break;
  635. }
  636. $num ++;
  637. }
  638. } else {
  639. throw new BCS_Exception ( "Can not find you seek object, please check!", - 1 );
  640. }
  641. }
  642. if (isset ( $opt ["seek_object_id"] )) {
  643. if (is_int ( $opt ["seek_object_id"] ) && $opt ["seek_object_id"] <= $objectCount) {
  644. foreach ( $objects as $object => $file ) {
  645. if ($num < $opt ["seek_object_id"]) {
  646. $this->log ( "Seeking object of [" . $opt ["seek_object_id"] . "/$objectCount]. Skip id[$num/$objectCount]object[$object]file[$file].", $opt );
  647. //$result ['skipped'] [] = "[$num/$objectCount] " . $file;
  648. $result ['skipped'] ++;
  649. unset ( $objects [$object] );
  650. } else {
  651. break;
  652. }
  653. $num ++;
  654. }
  655. } else {
  656. throw new BCS_Exception ( "Param seek_object_id not valid, please check!", - 1 );
  657. }
  658. }
  659. //上传objects
  660. $objectCount = count ( $objects );
  661. foreach ( $objects as $object => $file ) {
  662. $tmp_opt = array_merge ( $opt );
  663. if (isset ( $opt [self::IMPORT_BCS_PRE_FILTER] ) && function_exists ( $opt [self::IMPORT_BCS_PRE_FILTER] )) {
  664. $bolRes = $opt [self::IMPORT_BCS_PRE_FILTER] ( $bucket, $object, $file, $tmp_opt );
  665. if ($bolRes !== true) {
  666. $this->log ( "User pre_filter_function return un-true. Skip id[$num/$objectCount]object[$object]file[$file].", $opt );
  667. //$result ['skipped'] [] = "id[$num/$objectCount]object[$object]file[$file]";
  668. $result ['skipped'] ++;
  669. $num ++;
  670. continue;
  671. }
  672. }
  673. try {
  674. $response = $this->create_object ( $bucket, $object, $file, $tmp_opt );
  675. } catch ( Exception $e ) {
  676. $this->log ( $e->getMessage (), $opt );
  677. $this->log ( "Upload Failed id[$num/$objectCount]object[$object]file[$file].", $opt );
  678. $num ++;
  679. continue;
  680. }
  681. if ($response->isOK ()) {
  682. $result ["success"] ++;
  683. $this->log ( "Upload Success id[$num/$objectCount]object[$object]file[$file].", $opt );
  684. } else {
  685. $result ["failed"] [] = "id[$num/$objectCount]object[$object]file[$file]";
  686. $this->log ( "Upload Failed id[$num/$objectCount]object[$object]file[$file].", $opt );
  687. }
  688. if (isset ( $opt [self::IMPORT_BCS_POST_FILTER] ) && function_exists ( $opt [self::IMPORT_BCS_POST_FILTER] )) {
  689. $opt [self::IMPORT_BCS_POST_FILTER] ( $bucket, $object, $file, $tmp_opt, $response );
  690. }
  691. $num ++;
  692. }
  693. //打印日志并返回结果数组
  694. $result_str = "\r\n\r\nUpload $dir to $bucket finished!\r\n";
  695. $result_str .= "**********************************************************\r\n";
  696. $result_str .= "**********************Result Summary**********************\r\n";
  697. $result_str .= "**********************************************************\r\n";
  698. $result_str .= "Upload directory : [$dir]\r\n";
  699. $result_str .= "File num : [$objectCount]\r\n";
  700. $result_str .= "Success: \r\n\tNum: " . $result ["success"] . "\r\n";
  701. $result_str .= "Skipped:\r\n\tNum:" . $result ["skipped"] . "\r\n";
  702. // foreach ( $result ["skipped"] as $skip ) {
  703. // $result_str .= "\t$skip\r\n";
  704. // }
  705. $result_str .= "Failed:\r\n\tNum:" . count ( $result ["failed"] ) . "\r\n";
  706. foreach ( $result ["failed"] as $fail ) {
  707. $result_str .= "\t$fail\r\n";
  708. }
  709. if (isset ( $opt [self::IMPORT_BCS_LOG_METHOD] )) {
  710. $this->log ( $result_str, $opt );
  711. } else {
  712. echo $result_str;
  713. }
  714. return $result;
  715. }
  716. /**
  717. * 通过此方法以拷贝的方式创建object,object来源为$source
  718. * @param array $source (Required) object 来源
  719. * bucket(Required)
  720. * object(Required)
  721. * @param array $dest (Required) 待拷贝的目标object
  722. * bucket(Required)
  723. * object(Required)
  724. * @param array $opt (Optional)
  725. * source_tag 指定拷贝对象的版本号
  726. * @throws BCS_Exception
  727. * @return BCS_ResponseCore
  728. */
  729. public function copy_object($source, $dest, $opt = array()) {
  730. $this->assertParameterArray ( $opt );
  731. //valid source and dest
  732. if (empty ( $source ) || ! is_array ( $source ) || ! isset ( $source [self::BUCKET] ) || ! isset ( $source [self::OBJECT] )) {
  733. throw new BCS_Exception ( '$source invalid, please check!', - 1 );
  734. }
  735. if (empty ( $dest ) || ! is_array ( $dest ) || ! isset ( $dest [self::BUCKET] ) || ! isset ( $dest [self::OBJECT] ) || ! self::validate_bucket ( $dest [self::BUCKET] ) || ! self::validate_object ( $dest [self::OBJECT] )) {
  736. throw new BCS_Exception ( '$dest invalid, please check!', - 1 );
  737. }
  738. $opt [self::BUCKET] = $dest [self::BUCKET];
  739. $opt [self::OBJECT] = $dest [self::OBJECT];
  740. $opt [self::METHOD] = 'PUT';
  741. self::set_header_into_opt ( 'x-bs-copy-source', 'bs://' . $source [self::BUCKET] . $source [self::OBJECT], $opt );
  742. if (isset ( $opt ['source_tag'] )) {
  743. self::set_header_into_opt ( 'x-bs-copy-source-tag', $opt ['source_tag'], $opt );
  744. }
  745. $response = $this->authenticate ( $opt );
  746. $this->log ( $response->isOK () ? "Copy object success!" : "Copy object failed! Response: [" . $response->body . "]", $opt );
  747. return $response;
  748. }
  749. /**
  750. * 设置object的meta信息
  751. * @param string $bucket (Required)
  752. * @param string $object (Required)
  753. * @param array $opt (Optional)
  754. * 目前支持的meta信息如下:
  755. * Content-Type
  756. * Cache-Control
  757. * Content-Disposition
  758. * Content-Encoding
  759. * Content-MD5
  760. * Expires
  761. * @return BCS_ResponseCore
  762. */
  763. public function set_object_meta($bucket, $object, $meta, $opt = array()) {
  764. $this->assertParameterArray ( $opt );
  765. $this->assertParameterArray ( $meta );
  766. $opt [self::BUCKET] = $bucket;
  767. $opt [self::OBJECT] = $object;
  768. $opt [self::METHOD] = 'PUT';
  769. //利用copy_object接口来设置meta信息
  770. $source = "bs://$bucket$object";
  771. if (empty ( $meta )) {
  772. throw new BCS_Exception ( '$meta can not be empty! And $meta must be array.', - 1 );
  773. }
  774. foreach ( $meta as $header => $value ) {
  775. self::set_header_into_opt ( $header, $value, $opt );
  776. }
  777. $source = array (
  778. self::BUCKET => $bucket,
  779. self::OBJECT => $object );
  780. $response = $this->copy_object ( $source, $source, $opt );
  781. $this->log ( $response->isOK () ? "Set object meta success!" : "Set object meta failed! Response: [" . $response->body . "]", $opt );
  782. return $response;
  783. }
  784. /**
  785. * 获取object的acl
  786. * @param string $bucket (Required)
  787. * @param string $object (Required)
  788. * @param array $opt (Optional)
  789. * @throws BCS_Exception
  790. * @return BCS_ResponseCore
  791. */
  792. public function get_object_acl($bucket, $object, $opt = array()) {
  793. $this->assertParameterArray ( $opt );
  794. $opt [self::BUCKET] = $bucket;
  795. $opt [self::METHOD] = 'GET';
  796. $opt [self::OBJECT] = $object;
  797. $opt [self::QUERY_STRING] = array (
  798. self::ACL => 1 );
  799. $response = $this->authenticate ( $opt );
  800. $this->log ( $response->isOK () ? "Get object acl success!" : "Get object acl failed! Response: [" . $response->body . "]", $opt );
  801. return $response;
  802. }
  803. /**
  804. * 设置object的acl,有三种模式,
  805. * (1).设置详细json格式的acl;
  806. * a. $acl 为json的array
  807. * b. $acl 为json的string
  808. * (2).通过acl_type字段进行设置
  809. * a. $acl 为BaiduBCS::$ACL_ACTIONS中的字段
  810. * @param string $bucket (Required)
  811. * @param string $object (Required)
  812. * @param string|array $acl (Required)
  813. * @param array $opt (Optional)
  814. * @return BCS_ResponseCore
  815. */
  816. public function set_object_acl($bucket, $object, $acl, $opt = array()) {
  817. $this->assertParameterArray ( $opt );
  818. //analyze acl
  819. $result = $this->analyze_user_acl ( $acl );
  820. $opt = array_merge ( $opt, $result );
  821. $opt [self::BUCKET] = $bucket;
  822. $opt [self::METHOD] = 'PUT';
  823. $opt [self::OBJECT] = $object;
  824. $opt [self::QUERY_STRING] = array (
  825. self::ACL => 1 );
  826. $response = $this->authenticate ( $opt );
  827. $this->log ( $response->isOK () ? "Set object acl success!" : "Set object acl failed! Response: [" . $response->body . "]", $opt );
  828. return $response;
  829. }
  830. /**
  831. * 删除object
  832. * @param string $bucket (Required)
  833. * @param string $object (Required)
  834. * @param array $opt (Optional)
  835. * @throws BCS_Exception
  836. * @return BCS_ResponseCore
  837. */
  838. public function delete_object($bucket, $object, $opt = array()) {
  839. $this->assertParameterArray ( $opt );
  840. $opt [self::BUCKET] = $bucket;
  841. $opt [self::METHOD] = 'DELETE';
  842. $opt [self::OBJECT] = $object;
  843. $response = $this->authenticate ( $opt );
  844. $this->log ( $response->isOK () ? "Delete object success!" : "Delete object failed! Response: [" . $response->body . "]", $opt );
  845. return $response;
  846. }
  847. /**
  848. * 判断object是否存在
  849. * @param string $bucket (Required)
  850. * @param string $object (Required)
  851. * @param array $opt (Optional)
  852. * @throws BCS_Exception
  853. * @return boolean true|boolean false|BCS_ResponseCore
  854. * true:object存在
  855. * false:不存在
  856. * BCS_ResponseCore其他错误
  857. */
  858. public function is_object_exist($bucket, $object, $opt = array()) {
  859. $this->assertParameterArray ( $opt );
  860. $opt [self::BUCKET] = $bucket;
  861. $opt [self::METHOD] = 'HEAD';
  862. $opt [self::OBJECT] = $object;
  863. $response = $this->get_object_info ( $bucket, $object, $opt );
  864. if ($response->isOK ()) {
  865. return true;
  866. } elseif ($response->status === 404) {
  867. return false;
  868. }
  869. return $response;
  870. }
  871. /**
  872. * 获取文件信息,发送的为HTTP HEAD请求,文件信息都在http response的header中,不会提取文件的内容
  873. * @param string $bucket (Required)
  874. * @param string $object (Required)
  875. * @param array $opt (Optional)
  876. * @throws BCS_Exception
  877. * @return array BCS_ResponseCore
  878. */
  879. public function get_object_info($bucket, $object, $opt = array()) {
  880. $this->assertParameterArray ( $opt );
  881. $opt [self::BUCKET] = $bucket;
  882. $opt [self::METHOD] = 'HEAD';
  883. $opt [self::OBJECT] = $object;
  884. $response = $this->authenticate ( $opt );
  885. $this->log ( $response->isOK () ? "Get object info success!" : "Get object info failed! Response: [" . $response->body . "]", $opt );
  886. return $response;
  887. }
  888. /**
  889. * 下载object
  890. * @param string $bucket (Required)
  891. * @param string $object (Required)
  892. * @param array $opt (Optional)
  893. * fileWriteTo (Optional)直接将请求结果写入该文件,如果fileWriteTo文件存在,sdk进行重命名再存储
  894. * @throws BCS_Exception
  895. * @return BCS_ResponseCore
  896. */
  897. public function get_object($bucket, $object, $opt = array()) {
  898. $this->assertParameterArray ( $opt );
  899. //若fileWriteTo待写入的文件已经存在,需要进行重命名
  900. if (isset ( $opt ["fileWriteTo"] ) && file_exists ( $opt ["fileWriteTo"] )) {
  901. $original_file_write_to = $opt ["fileWriteTo"];
  902. $arr = explode ( DIRECTORY_SEPARATOR, $opt ["fileWriteTo"] );
  903. $file_name = $arr [count ( $arr ) - 1];
  904. $num = 1;
  905. while ( file_exists ( $opt ["fileWriteTo"] ) ) {
  906. $new_name_arr = explode ( ".", $file_name );
  907. if (count ( $new_name_arr ) > 1) {
  908. $new_name_arr [count ( $new_name_arr ) - 2] .= " ($num)";
  909. } else {
  910. $new_name_arr [0] .= " ($num)";
  911. }
  912. $arr [count ( $arr ) - 1] = implode ( ".", $new_name_arr );
  913. $opt ["fileWriteTo"] = implode ( DIRECTORY_SEPARATOR, $arr );
  914. $num ++;
  915. }
  916. $this->log ( "[$original_file_write_to] already exist, rename it to [" . $opt ["fileWriteTo"] . "]", $opt );
  917. }
  918. $opt [self::BUCKET] = $bucket;
  919. $opt [self::METHOD] = 'GET';
  920. $opt [self::OBJECT] = $object;
  921. $response = $this->authenticate ( $opt );
  922. $this->log ( $response->isOK () ? "Get object success!" : "Get object failed! Response: [" . $response->body . "]", $opt );
  923. if (! $response->isOK () && isset ( $opt ["fileWriteTo"] )) {
  924. unlink ( $opt ["fileWriteTo"] );
  925. }
  926. return $response;
  927. }
  928. /**
  929. * 生成签名链接
  930. */
  931. private function generate_user_url($method, $bucket, $object, $opt = array()) {
  932. $opt [self::AK] = $this->ak;
  933. $opt [self::SK] = $this->sk;
  934. $opt [self::BUCKET] = $bucket;
  935. $opt [self::METHOD] = $method;
  936. $opt [self::OBJECT] = $object;
  937. $opt [self::QUERY_STRING] = array ();
  938. if (isset ( $opt ["time"] )) {
  939. $opt [self::QUERY_STRING] ["time"] = $opt ["time"];
  940. }
  941. if (isset ( $opt ["size"] )) {
  942. $opt [self::QUERY_STRING] ["size"] = $opt ["size"];
  943. }
  944. return $this->format_url ( $opt );
  945. }
  946. /**
  947. * 生成get_object的url
  948. * @param string $bucket (Required)
  949. * @param string $object (Required)
  950. * return false| string url
  951. */
  952. public function generate_get_object_url($bucket, $object, $opt = array()) {
  953. $this->assertParameterArray ( $opt );
  954. return $this->generate_user_url ( "GET", $bucket, $object, $opt );
  955. }
  956. /**
  957. * 生成put_object的url
  958. * @param string $bucket (Required)
  959. * @param string $object (Required)
  960. * return false| string url
  961. */
  962. public function generate_put_object_url($bucket, $object, $opt = array()) {
  963. $this->assertParameterArray ( $opt );
  964. return $this->generate_user_url ( "PUT", $bucket, $object, $opt );
  965. }
  966. /**
  967. * 生成post_object的url
  968. * @param string $bucket (Required)
  969. * @param string $object (Required)
  970. * return false| string url
  971. */
  972. public function generate_post_object_url($bucket, $object, $opt = array()) {
  973. $this->assertParameterArray ( $opt );
  974. return $this->generate_user_url ( "POST", $bucket, $object, $opt );
  975. }
  976. /**
  977. * 生成delete_object的url
  978. * @param string $bucket (Required)
  979. * @param string $object (Required)
  980. * return false| string url
  981. */
  982. public function generate_delete_object_url($bucket, $object, $opt = array()) {
  983. $this->assertParameterArray ( $opt );
  984. return $this->generate_user_url ( "DELETE", $bucket, $object, $opt );
  985. }
  986. /**
  987. * 生成head_object的url
  988. * @param string $bucket (Required)
  989. * @param string $object (Required)
  990. * return false| string url
  991. */
  992. public function generate_head_object_url($bucket, $object, $opt = array()) {
  993. $this->assertParameterArray ( $opt );
  994. return $this->generate_user_url ( "HEAD", $bucket, $object, $opt );
  995. }
  996. /**
  997. * @return the $use_ssl
  998. */
  999. public function getUse_ssl() {
  1000. return $this->use_ssl;
  1001. }
  1002. /**
  1003. * @param boolean $use_ssl
  1004. */
  1005. public function setUse_ssl($use_ssl) {
  1006. $this->use_ssl = $use_ssl;
  1007. }
  1008. /**
  1009. * 校验bucket是否合法,bucket规范
  1010. * 1. 由小写字母,数字和横线'-'组成,长度为6~63
  1011. * 2. 不能以数字作为Bucket开头
  1012. * 3. 不能以'-'作为Bucket的开头或者结尾
  1013. * @param string $bucket
  1014. * @return boolean
  1015. */
  1016. public static function validate_bucket($bucket) {
  1017. //bucket 正则
  1018. $pattern1 = '/^[a-z][-a-z0-9]{4,61}[a-z0-9]$/';
  1019. if (! preg_match ( $pattern1, $bucket )) {
  1020. return false;
  1021. }
  1022. return true;
  1023. }
  1024. /**
  1025. * 校验object是否合法,object命名规范
  1026. * 1. object必须以'/'开头
  1027. * @param string $object
  1028. * @return boolean
  1029. */
  1030. public static function validate_object($object) {
  1031. $pattern = '/^\//';
  1032. if (empty ( $object ) || ! preg_match ( $pattern, $object )) {
  1033. return false;
  1034. }
  1035. return true;
  1036. }
  1037. /**
  1038. * 将常用set http-header的动作抽离出来
  1039. * @param string $header
  1040. * @param string $value
  1041. * @param array $opt
  1042. * @throws BCS_Exception
  1043. * @return void
  1044. */
  1045. private static function set_header_into_opt($header, $value, &$opt) {
  1046. if (isset ( $opt [self::HEADERS] )) {
  1047. if (! is_array ( $opt [self::HEADERS] )) {
  1048. trigger_error ( 'Invalid $opt[\'headers\'], please check.' );
  1049. throw new BCS_Exception ( 'Invalid $opt[\'headers\'], please check.', - 1 );
  1050. }
  1051. } else {
  1052. $opt [self::HEADERS] = array ();
  1053. }
  1054. $opt [self::HEADERS] [$header] = $value;
  1055. }
  1056. /**
  1057. * 使用特定function对数组中所有元素做处理
  1058. * @param string &$array 要处理的字符串
  1059. * @param string $function 要执行的函数
  1060. * @param boolean $apply_to_keys_also 是否也应用到key上
  1061. */
  1062. private static function array_recursive(&$array, $function, $apply_to_keys_also = false) {
  1063. foreach ( $array as $key => $value ) {
  1064. if (is_array ( $value )) {
  1065. self::array_recursive ( $array [$key], $function, $apply_to_keys_also );
  1066. } else {
  1067. $array [$key] = $function ( $value );
  1068. }
  1069. if ($apply_to_keys_also && is_string ( $key )) {
  1070. $new_key = $function ( $key );
  1071. if ($new_key != $key) {
  1072. $array [$new_key] = $array [$key];
  1073. unset ( $array [$key] );
  1074. }
  1075. }
  1076. }
  1077. }
  1078. /**
  1079. * 由数组构造json字符串,增加了一些特殊处理以支持特殊字符和不同编码的中文
  1080. * @param array $array
  1081. */
  1082. private static function array_to_json($array) {
  1083. if (! is_array ( $array )) {
  1084. throw new BCS_Exception ( "Param must be array in function array_to_json()", - 1 );
  1085. }
  1086. self::array_recursive ( $array, 'addslashes', false );
  1087. self::array_recursive ( $array, 'rawurlencode', false );
  1088. return rawurldecode ( json_encode ( $array ) );
  1089. }
  1090. /**
  1091. * 根据用户传入的acl,进行相应的处理
  1092. * (1).设置详细json格式的acl;
  1093. * a. $acl 为json的array
  1094. * b. $acl 为json的string
  1095. * (2).通过acl_type字段进行设置
  1096. * @param string|array $acl
  1097. * @throws BCS_Exception
  1098. * @return array
  1099. */
  1100. private function analyze_user_acl($acl) {
  1101. $result = array ();
  1102. if (is_array ( $acl )) {
  1103. //(1).a
  1104. $result ['content'] = $this->check_user_acl ( $acl );
  1105. } else if (is_string ( $acl )) {
  1106. if (in_array ( $acl, self::$ACL_TYPES )) {
  1107. //(2).a
  1108. $result ["headers"] = array (
  1109. "x-bs-acl" => $acl );
  1110. } else {
  1111. //(1).b
  1112. $result ['content'] = $acl;
  1113. }
  1114. } else {
  1115. throw new BCS_Exception ( "Invalid acl.", - 1 );
  1116. }
  1117. return $result;
  1118. }
  1119. /**
  1120. * 生成签名
  1121. * @param array $opt
  1122. * @return boolean|string
  1123. */
  1124. private function format_signature($opt) {
  1125. $flags = "";
  1126. $content = '';
  1127. if (! isset ( $opt [self::AK] ) || ! isset ( $opt [self::SK] )) {
  1128. trigger_error ( 'ak or sk is not in the array when create factor!' );
  1129. return false;
  1130. }
  1131. if (isset ( $opt [self::BUCKET] ) && isset ( $opt [self::METHOD] ) && isset ( $opt [self::OBJECT] )) {
  1132. $flags .= 'MBO';
  1133. $content .= "Method=" . $opt [self::METHOD] . "\n"; //method
  1134. $content .= "Bucket=" . $opt [self::BUCKET] . "\n"; //bucket
  1135. $content .= "Object=" . self::trimUrl ( $opt [self::OBJECT] ) . "\n"; //object
  1136. } else {
  1137. trigger_error ( 'bucket、method and object cann`t be NULL!' );
  1138. return false;
  1139. }
  1140. if (isset ( $opt ['ip'] )) {
  1141. $flags .= 'I';
  1142. $content .= "Ip=" . $opt ['ip'] . "\n";
  1143. }
  1144. if (isset ( $opt ['time'] )) {
  1145. $flags .= 'T';
  1146. $content .= "Time=" . $opt ['time'] . "\n";
  1147. }
  1148. if (isset ( $opt ['size'] )) {
  1149. $flags .= 'S';
  1150. $content .= "Size=" . $opt ['size'] . "\n";
  1151. }
  1152. $content = $flags . "\n" . $content;
  1153. $sign = base64_encode ( hash_hmac ( 'sha1', $content, $opt [self::SK], true ) );
  1154. return 'sign=' . $flags . ':' . $opt [self::AK] . ':' . urlencode ( $sign );
  1155. }
  1156. /**
  1157. * 检查用户输入的acl array是否合法,并转为json
  1158. * @param array $acl
  1159. * @throws BCS_Exception
  1160. * @return string acl-json
  1161. */
  1162. private function check_user_acl($acl) {
  1163. if (! is_array ( $acl )) {
  1164. throw new BCS_Exception ( "Invalid acl array" );
  1165. }
  1166. foreach ( $acl ['statements'] as $key => $statement ) {
  1167. // user resource action effect must in statement
  1168. if (! isset ( $statement ['user'] ) || ! isset ( $statement ['resource'] ) || ! isset ( $statement ['action'] ) || ! isset ( $statement ['effect'] )) {
  1169. throw new BCS_Exception ( 'Param miss: format acl error, please check your param!' );
  1170. }
  1171. if (! is_array ( $statement ['user'] ) || ! is_array ( $statement ['resource'] )) {
  1172. throw new BCS_Exception ( 'Param error: user or resource must be array, please check your param!' );
  1173. }
  1174. if (! is_array ( $statement ['action'] ) || ! count ( array_diff ( $statement ['action'], self::$ACL_ACTIONS ) ) == 0) {
  1175. throw new BCS_Exception ( 'Param error: action, please check your param!' );
  1176. }
  1177. if (! in_array ( $statement ['effect'], self::$ACL_EFFECTS )) {
  1178. throw new BCS_Exception ( 'Param error: effect, please check your param!' );
  1179. }
  1180. if (isset ( $statement ['time'] )) {
  1181. if (! is_array ( $statement ['time'] )) {
  1182. throw new BCS_Exception ( 'Param error: time, please check your param!' );
  1183. }
  1184. }
  1185. }
  1186. return self::array_to_json ( $acl );
  1187. }
  1188. /**
  1189. * 构造url
  1190. * @param array $opt
  1191. * @return boolean|string
  1192. */
  1193. private function format_url($opt) {
  1194. $sign = $this->format_signature ( $opt );
  1195. if ($sign === false) {
  1196. trigger_error ( "Format signature failed, please check!" );
  1197. return false;
  1198. }
  1199. $opt ['sign'] = $sign;
  1200. $url = "";
  1201. $url .= $this->use_ssl ? 'https://' : 'http://';
  1202. $url .= $this->hostname;
  1203. $url .= '/' . $opt [self::BUCKET];
  1204. if (isset ( $opt [self::OBJECT] ) && '/' !== $opt [self::OBJECT]) {
  1205. $url .= "/" . rawurlencode ( $opt [self::OBJECT] );
  1206. }
  1207. $url .= '?' . $sign;
  1208. if (isset ( $opt [self::QUERY_STRING] )) {
  1209. foreach ( $opt [self::QUERY_STRING] as $key => $value ) {
  1210. $url .= '&' . $key . '=' . $value;
  1211. }
  1212. }
  1213. return $url;
  1214. }
  1215. /**
  1216. * 将url中 '//' 替换为 '/'
  1217. * @param $url
  1218. * @return string
  1219. */
  1220. public static function trimUrl($url) {
  1221. $result = str_replace ( "//", "/", $url );
  1222. while ( $result !== $url ) {
  1223. $url = $result;
  1224. $result = str_replace ( "//", "/", $url );
  1225. }
  1226. return $result;
  1227. }
  1228. /**
  1229. * 获取传入目录的文件列表
  1230. * @param string $dir 文件目录
  1231. * @return array 文件树
  1232. */
  1233. public static function get_filetree($dir, $file_prefix = "/*") {
  1234. $tree = array ();
  1235. foreach ( glob ( $dir . $file_prefix ) as $single ) {
  1236. if (is_dir ( $single )) {
  1237. $tree = array_merge ( $tree, self::get_filetree ( $single ) );
  1238. } else {
  1239. $tree [] = $single;
  1240. }
  1241. }
  1242. return $tree;
  1243. }
  1244. /**
  1245. * 内置的日志函数,可以根据用户传入的log函数,进行日志输出
  1246. * @param string $log
  1247. * @param array $opt
  1248. */
  1249. public function log($log, $opt) {
  1250. if (isset ( $opt [self::IMPORT_BCS_LOG_METHOD] ) && function_exists ( $opt [self::IMPORT_BCS_LOG_METHOD] )) {
  1251. $opt [self::IMPORT_BCS_LOG_METHOD] ( $log );
  1252. } else {
  1253. trigger_error ( $log );
  1254. }
  1255. }
  1256. /**
  1257. * make sure $opt is an array
  1258. * @param $opt
  1259. */
  1260. private function assertParameterArray($opt) {
  1261. if (! is_array ( $opt )) {
  1262. throw new BCS_Exception ( 'Parameter must be array, please check!', - 1 );
  1263. }
  1264. }
  1265. }