bcs.class.php 47 KB

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