bcs.class.php 54 KB

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