GrandCloudStorage.php 70 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492
  1. <?php
  2. /**
  3. * =======================================================================
  4. * simple:
  5. * require_once (dirname(__FILE__).'/GrandCloudStorage.php');
  6. *
  7. * $client = new GrandCloudStorage($host);
  8. * $client->set_key_secret($access_key, $access_secret);
  9. *
  10. * $bucket_name = "test_bucket";
  11. * $client->put_bucket($bucket_name); // create new bucket
  12. *
  13. * $client->set_bucket($bucket_name); // set $bucket_name as default for follow ops
  14. * $client->put_object("test.ext", "localfile.ext"); // upload localfile.ext file to $bucket_name and assign name as text.ext
  15. * $client->head_object("test.ext"); // get test.ext object's meta
  16. * $client->get_object("test.ext", "tmp.ext"); // download test.ext object as local tmp.ext file
  17. * $client->delete_object("test.ext"); // delete test.ext object
  18. * =======================================================================
  19. */
  20. // Errors
  21. define('ERR_ACCESS_DENIED', 'AccessDenied');
  22. define('ERR_UNSUPPORTED_TRANSFER_ENCODING', 'UnsupportedTransferEncoding');
  23. define('ERR_BAD_DIGEST', 'BadDigest');
  24. define('ERR_INCOMPLETE_BODY', 'IncompleteBody');
  25. define('ERR_BUCKET_ACCESS_DENIED', 'BucketAccessDenied');
  26. define('ERR_BUCKET_NOT_EMPTY', 'BucketNotEmpty');
  27. define('ERR_BUCKET_UNEXIST', 'NoSuchBucket');
  28. define('ERR_BUCKET_TOO_MANY', 'TooManyBuckets');
  29. define('ERR_BUCKET_NAME_CONFLICT', 'BucketAlreadyExists');
  30. define('ERR_BUCKET_NAME_INVALID', 'InvalidBucketName');
  31. define('ERR_BUCKET_NOPOLICY', 'NotSuchBucketPolicy');
  32. define('ERR_ENTITY_TOO_LARGE', 'EntityTooLarge');
  33. define('ERR_OBJECT_KEY_TOO_LONG', 'KeyTooLong');
  34. define('ERR_OBJECT_UNEXIST', 'NoSuchKey');
  35. define('ERR_INVALID_ACCESS_KEY', 'InvalidAccessKeyId');
  36. define('ERR_INVALID_CONTENT_LENGTH', 'InvalidContentLength');
  37. define('ERR_INVALID_EXPIRES', 'InvalidExpires');
  38. define('ERR_INVALID_RANGE', 'InvalidRange');
  39. define('ERR_INVALID_REQUEST_TIME', 'InvalidRequestTime');
  40. define('ERR_INVALID_USER_METADATA', 'InvalidUserMetadata');
  41. define('ERR_MALFORMED_AUTHORIZATION', 'MalformedAuthorization');
  42. define('ERR_MALFORMED_XML', 'MalformedXML');
  43. define('ERR_METHOD_NOT_ALLOWED', 'MethodNotAllowed');
  44. define('ERR_MISSING_CONTENT_LENGTH', 'MissingContentLength');
  45. define('ERR_MISSING_SECURITY_HEADER', 'MissingSecurityHeader');
  46. define('ERR_MULTIPLE_RANGE', 'MultipleRange');
  47. define('ERR_PRECONDITION_FAILED', 'PreconditionFailed');
  48. define('ERR_REQUEST_EXPIRED', 'RequestHasExpired');
  49. define('ERR_REQUEST_TIMEOUT', 'RequestTimeout');
  50. define('ERR_REQUEST_TIMESKEWED', 'RequestTimeTooSkewed');
  51. define('ERR_SIGNATURE_UNMATCH', 'SignatureDoesNotMatch');
  52. // Bucket对象
  53. class GCBucket {
  54. protected $idc; // bucket所在IDC,可用值为:huadong-1,huabei-1
  55. protected $name; // bucket名称
  56. protected $ctime; // bucket创建时间,参见:date('r')
  57. public function __construct($idc, $name, $ctime) {
  58. $this->idc = $idc;
  59. $this->name = $name;
  60. $this->ctime = $ctime;
  61. return $this;
  62. }
  63. public function get_idc() {
  64. return $this->idc;
  65. }
  66. public function get_name() {
  67. return $this->name;
  68. }
  69. public function get_ctime() {
  70. return $this->ctime;
  71. }
  72. public function to_array() {
  73. return array(
  74. 'idc' => $this->idc,
  75. 'name' => $this->name,
  76. 'ctime' => $this->ctime
  77. );
  78. }
  79. }
  80. // Object对象
  81. class GCObject {
  82. protected $key; // object key
  83. protected $size; // object size
  84. protected $etag; // object ETAG
  85. protected $last_modified; // object last modified time
  86. public function __construct($key, $size, $last_modified, $etag) {
  87. $this->key = $key;
  88. $this->size = $size;
  89. $this->etag = $etag;
  90. $this->last_modified = $last_modified;
  91. return $this;
  92. }
  93. public function get_key() {
  94. return $this->key;
  95. }
  96. public function get_size() {
  97. return $this->size;
  98. }
  99. public function get_etag() {
  100. return $this->etag;
  101. }
  102. public function get_last_modified() {
  103. return $this->last_modified;
  104. }
  105. public function to_array() {
  106. return array(
  107. 'key' => $this->key,
  108. 'size' => $this->size,
  109. 'etag' => $this->etag,
  110. 'last_modified' => $this->last_modified
  111. );
  112. }
  113. }
  114. class GCUploadPart extends GCObject {
  115. protected $partnumber;// part number of this part
  116. /**Tags used in parse xml*/
  117. public static $partnumberTag = "PartNumber";
  118. public static $lastModifiedTag = "LastModified";
  119. public static $etagTag = "ETag";
  120. public static $sizeTag = "Size";
  121. public static $keyTag = "Key";
  122. public static $partTag = "Part";
  123. public function __construct($key, $size, $last_modified, $etag,$part_number) {
  124. parent::__construct($key, $size, $last_modified, $etag);
  125. $this->partnumber = $part_number;
  126. }
  127. public function get_partnumber() {
  128. return $this->partnumber;
  129. }
  130. public function to_array() {
  131. return array(
  132. GCUploadPart::$keyTag => $this->key,
  133. GCUploadPart::$partnumberTag => $this->partnumber,
  134. GCUploadPart::$lastModifiedTag => $this->last_modified,
  135. GCUploadPart::$etagTag => $this->etag,
  136. GCUploadPart::$sizeTag => $this->size
  137. );
  138. }
  139. public function to_xml_for_completemultipartupload() {
  140. $xml = "<PartNumber>{$this->partnumber}</PartNumber>";
  141. $xml .= "<ETag>{$this->etag}</ETag>";
  142. return $xml;
  143. }
  144. }
  145. /**
  146. * GCMUltipartUpload
  147. * @author fun
  148. *
  149. */
  150. class GCMultipartUpload {
  151. protected $bucket;// bucket's name
  152. protected $key;//the object name of this Multipart Upload
  153. protected $uploadid;//id used to identify multipartupload
  154. protected $initiated; //when this upload initiated
  155. /**Tags used in parse xml body*/
  156. public static $InitiateMultipartUploadResultTag = "InitiateMultipartUploadResult";
  157. public static $bucketTag = "Bucket";
  158. public static $keyTag = "Key";
  159. public static $uploadIdTag = "UploadId";
  160. public static $initiatedTag = "Initiated";
  161. public function __construct($bucket,$key,$uploadid,$initiated = '') {
  162. $this->bucket = $bucket;
  163. $this->key = $key;
  164. $this->uploadid = $uploadid;
  165. $this->initiated = $initiated;
  166. }
  167. public function get_bucket() {
  168. return $this->bucket;
  169. }
  170. public function get_key() {
  171. return $this->key;
  172. }
  173. public function get_uploadid() {
  174. return $this->uploadid;
  175. }
  176. public function get_initated() {
  177. return $this->initiated;
  178. }
  179. public function to_array() {
  180. $meta_data =array(
  181. GCMultipartUpload::$bucketTag => $this->bucket,
  182. GCMultipartUpload::$keyTag => $this->key,
  183. GCMultipartUpload::$uploadIdTag => $this->uploadid
  184. );
  185. if (! empty($this->initiated)) {
  186. $meta_data[GCMultipartUpload::$initiatedTag] = $this->initiated;
  187. }
  188. return $meta_data;
  189. }
  190. }
  191. // Entity对象
  192. class GCEntity {
  193. protected $bucket; // bucket名称
  194. protected $prefix = ''; // 获取对象时前缀过滤字符串
  195. protected $marker = ''; // 获取对象时偏移对象的名称
  196. protected $maxkeys; // 获取对象时返回的最大记录数
  197. protected $delimiter; // 获取对象时使用的分隔符
  198. protected $istruncated = false; // 返回结果是否经过截短?
  199. protected $objectarray = array(); // object list array
  200. public function __construct() {
  201. return $this;
  202. }
  203. public function set_bucket($bucket) {
  204. $this->bucket = $bucket;
  205. }
  206. public function get_bucket() {
  207. return $this->bucket;
  208. }
  209. public function set_prefix($prefix) {
  210. $this->prefix = $prefix;
  211. }
  212. public function get_prefix() {
  213. return $this->prefix;
  214. }
  215. public function set_marker($marker) {
  216. $this->marker = $marker;
  217. }
  218. public function get_marker() {
  219. return $this->marker;
  220. }
  221. public function set_maxkeys($maxkeys) {
  222. $this->maxkeys = $maxkeys;
  223. }
  224. public function get_maxkeys() {
  225. return $this->maxkeys;
  226. }
  227. public function set_delimiter($delimiter) {
  228. $this->delimiter = $delimiter;
  229. }
  230. public function get_delimiter() {
  231. return $this->delimiter;
  232. }
  233. public function set_istruncated($istruncated) {
  234. $this->istruncated = $istruncated;
  235. }
  236. public function get_istruncated() {
  237. return $this->istruncated;
  238. }
  239. public function add_object($object) {
  240. $this->objectarray[]= $object;
  241. }
  242. public function get_object($idx=null) {
  243. if ($idx === null) {
  244. return $this->objectarray;
  245. }
  246. $max = count($this->objectarray);
  247. $idx = intval($idx);
  248. if ($idx < 0) {
  249. $idx += $max;
  250. }
  251. if ($idx >= 0 && $idx < $max) {
  252. return $this->objectarray[$idx];
  253. }
  254. return null;
  255. }
  256. public function to_array() {
  257. return array(
  258. 'bucket' => $this->bucket,
  259. 'prefix' => $this->prefix,
  260. 'marker' => $this->marker,
  261. 'maxkeys' => $this->maxkeys,
  262. 'delimiter' => $this->delimiter,
  263. 'istruncated' => $this->istruncated,
  264. 'object' => $this->objectarray
  265. );
  266. }
  267. }
  268. class GCMultipartUploadEntity extends GCEntity {
  269. protected $uploadidmarker = '';
  270. protected $nextkeymarker = '';
  271. protected $nextuploadidmarker = '';
  272. /**Tags of List Multipart Uploads*/
  273. public static $listMultipartUploadsResultTag = "ListMultipartUploadsResult";
  274. public static $bucketTag = "Bucket";
  275. public static $delimiterTag = "Delimiter";
  276. public static $prefixTag = "Prefix";
  277. public static $maxUploadsTag = "MaxUploads";
  278. public static $keyMarkerTag = "KeyMarker";
  279. public static $uploadIdMarker = "UploadIdMarker";
  280. public static $nextKeyMarkerTag = "NextKeyMarker";
  281. public static $nextUploadIdMarkerTag = "NextUploadIdMarker";
  282. public static $isTruncatedTag = "IsTruncated";
  283. public static $uploadTag = "Upload";
  284. public static $commonPrefixesTag = "CommonPrefixes";
  285. public function __construct() {
  286. parent::__construct();
  287. return $this;
  288. }
  289. public function set_maxUploads($maxuploads) {
  290. parent::set_maxkeys($maxuploads);
  291. }
  292. public function get_maxUploads() {
  293. return parent::get_maxkeys();
  294. }
  295. public function set_keyMarker( $keymarker ) {
  296. parent::set_marker($keymarker);
  297. }
  298. public function getKeyMarker() {
  299. return parent::get_marker();
  300. }
  301. public function set_uploadIdMarker($uploadIdMarker) {
  302. $this->uploadidmarker = $uploadIdMarker;
  303. }
  304. public function get_uploadIdMarker() {
  305. return $this->uploadidmarker;
  306. }
  307. public function set_nextKeyMarker( $nextKeyMarker ) {
  308. $this->nextkeymarker = $nextKeyMarker;
  309. }
  310. public function get_nextKeyMarker() {
  311. return $this->nextkeymarker;
  312. }
  313. public function set_nextUploadIdMarker( $nextuploadIdMarker ) {
  314. $this->nextuploadidmarker = $nextuploadIdMarker;
  315. }
  316. public function get_nextUploadIdMarker() {
  317. return $this->nextuploadidmarker;
  318. }
  319. public function addUpload( $upload ) {
  320. parent::add_object($upload);
  321. }
  322. public function get_upload($idx = null) {
  323. return parent::get_object($idx);
  324. }
  325. public function to_array(){
  326. return array(
  327. GCMultipartUploadEntity::$bucketTag => $this->bucket,
  328. GCMultipartUploadEntity::$prefixTag => $this->prefix,
  329. GCMultipartUploadEntity::$keyMarkerTag => $this->marker,
  330. GCMultipartUploadEntity::$maxUploadsTag => $this->maxkeys,
  331. GCMultipartUploadEntity::$delimiterTag => $this->delimiter,
  332. GCMultipartUploadEntity::$uploadIdMarker => $this->uploadidmarker,
  333. GCMultipartUploadEntity::$nextKeyMarkerTag => $this->nextkeymarker,
  334. GCMultipartUploadEntity::$nextUploadIdMarkerTag => $this->nextuploadidmarker,
  335. GCMultipartUploadEntity::$isTruncatedTag => $this->istruncated,
  336. GCMultipartUploadEntity::$uploadTag => $this->objectarray
  337. );
  338. }
  339. }
  340. //Entity used in list parts
  341. class GCPartsEntity {
  342. protected $bucket = ""; // bucket's name
  343. protected $key = ""; // multipart upload's key
  344. protected $uploadid = "";
  345. protected $maxparts = ""; // the maximum number of parts returned in the response body
  346. protected $istruncated = "";//$part_number_marker,the part to start with
  347. protected $partnumbermarker = "";// part number to start with
  348. protected $nextpartnumbermarker = "";// next part number to start with
  349. protected $partsarray = array();
  350. /**Tags used in parse listpart result*/
  351. public static $listpartsresultTag = "ListPartsResult";
  352. public static $bucketTag = "Bucket";
  353. public static $keyTag = "Key";
  354. public static $uploadIdTag = "UploadId";
  355. public static $maxpartsTag = "MaxParts";
  356. public static $istruncatedTag = "IsTruncated";
  357. public static $partnumberMarkerTag = "PartNumberMarker";
  358. public static $nextpartnumbermarkerTag = "NextPartNumberMarker";
  359. public static $partTag = "Part";
  360. public function __construct() {
  361. return $this;
  362. }
  363. public function set_bucket($bucket) {
  364. $this->bucket = $bucket;
  365. }
  366. public function get_bucket() {
  367. return $this->bucket;
  368. }
  369. public function set_key ($key) {
  370. $this->key = $key;
  371. }
  372. public function get_key(){
  373. return $this->key;
  374. }
  375. public function set_uploadid($uploadid) {
  376. $this->uploadid = $uploadid;
  377. }
  378. public function get_uploadid() {
  379. return $this->uploadid;
  380. }
  381. public function set_maxparts( $maxparts ) {
  382. $this->maxparts = $maxparts;
  383. }
  384. public function get_maxparts() {
  385. return $this->maxparts;
  386. }
  387. public function set_istruncated( $istruncated ) {
  388. $this->istruncated = $istruncated;
  389. }
  390. public function get_istruncated () {
  391. return $this->istruncated;
  392. }
  393. public function set_partnumbermarker( $partnumbermarker ){
  394. $this->partnumbermarker = $partnumbermarker;
  395. }
  396. public function get_partnumbermarker() {
  397. return $this->partnumbermarker;
  398. }
  399. public function set_nextpartnumbermarker( $nextpartnumbermarker ) {
  400. $this->nextpartnumbermarker = $nextpartnumbermarker;
  401. }
  402. public function get_nextpartnumbermarker(){
  403. return $this->nextpartnumbermarker;
  404. }
  405. public function add_part( $part ) {
  406. $this->partsarray[] = $part;
  407. }
  408. public function get_part($idx = null) {
  409. if (null === $idx) {
  410. return $this->partsarray;
  411. }
  412. $max = count($this->partsarray);
  413. $idx = intval($idx);
  414. if ($idx < 0) {
  415. $idx += $max;
  416. }
  417. if ($idx >= 0 && $idx < $max) {
  418. return $this->partsarray[$idx];
  419. }
  420. return null;
  421. }
  422. public function to_array() {
  423. return array(
  424. GCPartsEntity::$bucketTag => $this->bucket,
  425. GCPartsEntity::$keyTag => $this->key,
  426. GCPartsEntity::$uploadIdTag => $this->uploadid,
  427. GCPartsEntity::$maxpartsTag => $this->maxparts,
  428. GCPartsEntity::$istruncatedTag => $this->istruncated,
  429. GCPartsEntity::$partnumberMarkerTag => $this->partnumbermarker,
  430. GCPartsEntity::$nextpartnumbermarkerTag => $this->nextpartnumbermarker,
  431. GCPartsEntity::$partTag => $this->partsarray
  432. );
  433. }
  434. /**
  435. * build complete multipart upload xml
  436. */
  437. public function to_completemultipartuploadxml() {
  438. $parts_xml = '<?xml version="1.0" encoding="UTF-8"?>';
  439. $parts_xml .= "<CompleteMultipartUpload>";
  440. foreach($this->partsarray as $part) {
  441. $parts_xml .= "<Part>";
  442. $parts_xml .= $part->to_xml_for_completemultipartupload();
  443. $parts_xml .= "</Part>";
  444. }
  445. $parts_xml .= "</CompleteMultipartUpload>";
  446. return $parts_xml;
  447. }
  448. }
  449. // MIME对象
  450. class GCMIME {
  451. /**
  452. * MIME map of the file extensions.
  453. */
  454. protected static $mime_maps = array(
  455. '3gp' => 'video/3gpp',
  456. 'ai' => 'application/postscript',
  457. 'aif' => 'audio/x-aiff',
  458. 'aifc' => 'audio/x-aiff',
  459. 'aiff' => 'audio/x-aiff',
  460. 'asc' => 'text/plain',
  461. 'atom' => 'application/atom+xml',
  462. 'au' => 'audio/basic',
  463. 'avi' => 'video/x-msvideo',
  464. 'bcpio' => 'application/x-bcpio',
  465. 'bin' => 'application/octet-stream',
  466. 'bmp' => 'image/bmp',
  467. 'cdf' => 'application/x-netcdf',
  468. 'cgm' => 'image/cgm',
  469. 'class' => 'application/octet-stream',
  470. 'cpio' => 'application/x-cpio',
  471. 'cpt' => 'application/mac-compactpro',
  472. 'csh' => 'application/x-csh',
  473. 'css' => 'text/css',
  474. 'dcr' => 'application/x-director',
  475. 'dif' => 'video/x-dv',
  476. 'dir' => 'application/x-director',
  477. 'djv' => 'image/vnd.djvu',
  478. 'djvu' => 'image/vnd.djvu',
  479. 'dll' => 'application/octet-stream',
  480. 'dmg' => 'application/octet-stream',
  481. 'dms' => 'application/octet-stream',
  482. 'doc' => 'application/msword',
  483. 'dtd' => 'application/xml-dtd',
  484. 'dv' => 'video/x-dv',
  485. 'dvi' => 'application/x-dvi',
  486. 'dxr' => 'application/x-director',
  487. 'eps' => 'application/postscript',
  488. 'etx' => 'text/x-setext',
  489. 'exe' => 'application/octet-stream',
  490. 'ez' => 'application/andrew-inset',
  491. 'flv' => 'video/x-flv',
  492. 'gif' => 'image/gif',
  493. 'gram' => 'application/srgs',
  494. 'grxml' => 'application/srgs+xml',
  495. 'gtar' => 'application/x-gtar',
  496. 'gz' => 'application/x-gzip',
  497. 'hdf' => 'application/x-hdf',
  498. 'hqx' => 'application/mac-binhex40',
  499. 'htm' => 'text/html',
  500. 'html' => 'text/html',
  501. 'ice' => 'x-conference/x-cooltalk',
  502. 'ico' => 'image/x-icon',
  503. 'ics' => 'text/calendar',
  504. 'ief' => 'image/ief',
  505. 'ifb' => 'text/calendar',
  506. 'iges' => 'model/iges',
  507. 'igs' => 'model/iges',
  508. 'jnlp' => 'application/x-java-jnlp-file',
  509. 'jp2' => 'image/jp2',
  510. 'jpe' => 'image/jpeg',
  511. 'jpeg' => 'image/jpeg',
  512. 'jpg' => 'image/jpeg',
  513. 'js' => 'application/x-javascript',
  514. 'kar' => 'audio/midi',
  515. 'latex' => 'application/x-latex',
  516. 'lha' => 'application/octet-stream',
  517. 'lzh' => 'application/octet-stream',
  518. 'm3u' => 'audio/x-mpegurl',
  519. 'm4a' => 'audio/mp4a-latm',
  520. 'm4p' => 'audio/mp4a-latm',
  521. 'm4u' => 'video/vnd.mpegurl',
  522. 'm4v' => 'video/x-m4v',
  523. 'mac' => 'image/x-macpaint',
  524. 'man' => 'application/x-troff-man',
  525. 'mathml' => 'application/mathml+xml',
  526. 'me' => 'application/x-troff-me',
  527. 'mesh' => 'model/mesh',
  528. 'mid' => 'audio/midi',
  529. 'midi' => 'audio/midi',
  530. 'mif' => 'application/vnd.mif',
  531. 'mov' => 'video/quicktime',
  532. 'movie' => 'video/x-sgi-movie',
  533. 'mp2' => 'audio/mpeg',
  534. 'mp3' => 'audio/mpeg',
  535. 'mp4' => 'video/mp4',
  536. 'mpe' => 'video/mpeg',
  537. 'mpeg' => 'video/mpeg',
  538. 'mpg' => 'video/mpeg',
  539. 'mpga' => 'audio/mpeg',
  540. 'ms' => 'application/x-troff-ms',
  541. 'msh' => 'model/mesh',
  542. 'mxu' => 'video/vnd.mpegurl',
  543. 'nc' => 'application/x-netcdf',
  544. 'oda' => 'application/oda',
  545. 'ogg' => 'application/ogg',
  546. 'ogv' => 'video/ogv',
  547. 'pbm' => 'image/x-portable-bitmap',
  548. 'pct' => 'image/pict',
  549. 'pdb' => 'chemical/x-pdb',
  550. 'pdf' => 'application/pdf',
  551. 'pgm' => 'image/x-portable-graymap',
  552. 'pgn' => 'application/x-chess-pgn',
  553. 'pic' => 'image/pict',
  554. 'pict' => 'image/pict',
  555. 'png' => 'image/png',
  556. 'pnm' => 'image/x-portable-anymap',
  557. 'pnt' => 'image/x-macpaint',
  558. 'pntg' => 'image/x-macpaint',
  559. 'ppm' => 'image/x-portable-pixmap',
  560. 'ppt' => 'application/vnd.ms-powerpoint',
  561. 'ps' => 'application/postscript',
  562. 'qt' => 'video/quicktime',
  563. 'qti' => 'image/x-quicktime',
  564. 'qtif' => 'image/x-quicktime',
  565. 'ra' => 'audio/x-pn-realaudio',
  566. 'ram' => 'audio/x-pn-realaudio',
  567. 'ras' => 'image/x-cmu-raster',
  568. 'rdf' => 'application/rdf+xml',
  569. 'rgb' => 'image/x-rgb',
  570. 'rm' => 'application/vnd.rn-realmedia',
  571. 'roff' => 'application/x-troff',
  572. 'rtf' => 'text/rtf',
  573. 'rtx' => 'text/richtext',
  574. 'sgm' => 'text/sgml',
  575. 'sgml' => 'text/sgml',
  576. 'sh' => 'application/x-sh',
  577. 'shar' => 'application/x-shar',
  578. 'silo' => 'model/mesh',
  579. 'sit' => 'application/x-stuffit',
  580. 'skd' => 'application/x-koan',
  581. 'skm' => 'application/x-koan',
  582. 'skp' => 'application/x-koan',
  583. 'skt' => 'application/x-koan',
  584. 'smi' => 'application/smil',
  585. 'smil' => 'application/smil',
  586. 'snd' => 'audio/basic',
  587. 'so' => 'application/octet-stream',
  588. 'spl' => 'application/x-futuresplash',
  589. 'src' => 'application/x-wais-source',
  590. 'sv4cpio' => 'application/x-sv4cpio',
  591. 'sv4crc' => 'application/x-sv4crc',
  592. 'svg' => 'image/svg+xml',
  593. 'swf' => 'application/x-shockwave-flash',
  594. 't' => 'application/x-troff',
  595. 'tar' => 'application/x-tar',
  596. 'tcl' => 'application/x-tcl',
  597. 'tex' => 'application/x-tex',
  598. 'texi' => 'application/x-texinfo',
  599. 'texinfo' => 'application/x-texinfo',
  600. 'tif' => 'image/tiff',
  601. 'tiff' => 'image/tiff',
  602. 'tr' => 'application/x-troff',
  603. 'tsv' => 'text/tab-separated-values',
  604. 'txt' => 'text/plain',
  605. 'ustar' => 'application/x-ustar',
  606. 'vcd' => 'application/x-cdlink',
  607. 'vrml' => 'model/vrml',
  608. 'vxml' => 'application/voicexml+xml',
  609. 'wav' => 'audio/x-wav',
  610. 'wbmp' => 'image/vnd.wap.wbmp',
  611. 'wbxml' => 'application/vnd.wap.wbxml',
  612. 'webm' => 'video/webm',
  613. 'wml' => 'text/vnd.wap.wml',
  614. 'wmlc' => 'application/vnd.wap.wmlc',
  615. 'wmls' => 'text/vnd.wap.wmlscript',
  616. 'wmlsc' => 'application/vnd.wap.wmlscriptc',
  617. 'wmv' => 'video/x-ms-wmv',
  618. 'wrl' => 'model/vrml',
  619. 'xbm' => 'image/x-xbitmap',
  620. 'xht' => 'application/xhtml+xml',
  621. 'xhtml' => 'application/xhtml+xml',
  622. 'xls' => 'application/vnd.ms-excel',
  623. 'xml' => 'application/xml',
  624. 'xpm' => 'image/x-xpixmap',
  625. 'xsl' => 'application/xml',
  626. 'xslt' => 'application/xslt+xml',
  627. 'xul' => 'application/vnd.mozilla.xul+xml',
  628. 'xwd' => 'image/x-xwindowdump',
  629. 'xyz' => 'chemical/x-xyz',
  630. 'zip' => 'application/zip'
  631. );
  632. /**
  633. * Get file MIME according to its extension.
  634. *
  635. * @param string $ext
  636. * @return string
  637. */
  638. public static function get_type($ext) {
  639. return isset(self::$mime_maps[$ext]) ? self::$mime_maps[$ext] : 'application/octet-stream';
  640. }
  641. }
  642. // Error对象
  643. class GCError extends Exception {
  644. protected $requestId; // the request sign, used for error trace
  645. protected $requestResource; // the request target resource
  646. protected $errorCode; // error code
  647. protected $errorMessage; // error message
  648. public function __construct($response_code, $response_xml) {
  649. if (!empty($response_xml)) {
  650. $this->parse_errxml($response_xml);
  651. }
  652. parent::__construct($this->errorMessage, $response_code);
  653. }
  654. public function getId() {
  655. return $this->requestId;
  656. }
  657. public function getResource() {
  658. return $this->requestResource;
  659. }
  660. public function getErrorCode() {
  661. return $this->errorCode;
  662. }
  663. public function getErrorMessage() {
  664. return $this->errorMessage;
  665. }
  666. public function to_array() {
  667. return array(
  668. 'code' => $this->code,
  669. 'message' => $this->message,
  670. 'errorCode' => $this->errorCode,
  671. 'errorMessage' => $this->errorMessage,
  672. 'requestId' => $this->requestId,
  673. 'requestResource' => $this->requestResource
  674. );
  675. }
  676. protected function parse_errxml($error_xml) {
  677. $error_xml = $this->get_xmlpart($error_xml);
  678. $doc = new DOMDocument();
  679. $doc->loadXML($error_xml);
  680. $errorCode = $doc->getElementsByTagName('Code')->item(0);
  681. $this->errorCode = empty($errorCode) ? 'UnknownErrorCode' : $errorCode->nodeValue;
  682. $errorMessage = $doc->getElementsByTagName('Message')->item(0);
  683. $this->errorMessage = empty($errorMessage) ? 'UnknownErrorMessage' : $errorMessage->nodeValue;
  684. $requestId = $doc->getElementsByTagName('RequestId')->item(0);
  685. $this->requestId = empty($requestId) ? 'UnknownRequestId' : $requestId->nodeValue;
  686. $requestResource = $doc->getElementsByTagName('Resource')->item(0);
  687. $this->requestResource = empty($requestResource) ? 'UnknownRequestResource' : $requestResource->nodeValue;
  688. }
  689. /**
  690. * Get xml part from response body
  691. */
  692. protected function get_xmlpart($response_body) {
  693. $tmparray = explode("\r\n\r\n", $response_body);
  694. $realbody = array();
  695. for($i=0;$i<count($tmparray);$i++) {
  696. $tmp = trim($tmparray[$i]);
  697. //printf("\nvc".substr($tmp,0,strlen("<?xml"))."\n");
  698. if(substr($tmp,0,strlen("<?xml")) === "<?xml") {
  699. break;
  700. }
  701. }
  702. for(;$i<count($tmparray);$i++) {
  703. $realbody[]=$tmparray[$i];
  704. }
  705. $realxml = implode("\r\n\r\n",$realbody);
  706. // printf("realxml:\n".$realxml."\n");
  707. return $realxml;
  708. }
  709. }
  710. class GrandCloudStorage {
  711. /**
  712. * GrandCloud domain
  713. * @access protected
  714. */
  715. protected $host;
  716. /**
  717. * access_key
  718. * @access protected
  719. */
  720. protected $access_key;
  721. /**
  722. * access_secret
  723. * @access protected
  724. */
  725. protected $access_secret;
  726. /**
  727. * bucket name
  728. * @access protected
  729. */
  730. protected $bucket;
  731. /**
  732. * bucket cname
  733. * this is used for public access control
  734. *
  735. * @access protected
  736. */
  737. protected $bucket_cname;
  738. /**
  739. * http headers, array
  740. * @access protected
  741. */
  742. protected $headers;
  743. /**
  744. * http body, string
  745. * @access protected
  746. */
  747. protected $body;
  748. /**
  749. * http response code
  750. * @access protected
  751. */
  752. protected $response_code;
  753. /**
  754. * http response header
  755. * @access protected
  756. */
  757. protected $response_header;
  758. /**
  759. * http response content length
  760. * @access protected
  761. */
  762. protected $response_length;
  763. /**
  764. * http response content text
  765. * @access protected
  766. */
  767. protected $response_body;
  768. /**
  769. * last curl error
  770. * @access protected
  771. */
  772. protected $last_curl_error;
  773. /**
  774. * debug switch
  775. * @access protected
  776. */
  777. protected $debug = false;
  778. /**
  779. * default location
  780. */
  781. const DEFAULT_LOCATION = 'huadong-1';
  782. /**
  783. * constructor
  784. * @param string $host storage host, no ending slash
  785. * @param string $bucket default bucket
  786. * @return $this object
  787. */
  788. public function __construct($host='', $bucket=null) {
  789. $this->host = $host;
  790. $this->bucket = $bucket;
  791. return $this;
  792. }
  793. /**
  794. * set region host
  795. * @param string $host
  796. * @return $this object
  797. */
  798. public function set_host($host) {
  799. $this->host = $host;
  800. return $this;
  801. }
  802. /**
  803. * get current region host
  804. * @param void
  805. * @return string
  806. */
  807. public function get_host() {
  808. return $this->host;
  809. }
  810. /**
  811. * set access_key and access_secret
  812. * @param string $access_key
  813. * @param string $access_secret
  814. * @return $this object
  815. */
  816. public function set_key_secret($access_key, $access_secret) {
  817. $this->access_key = $access_key;
  818. $this->access_secret = $access_secret;
  819. return $this;
  820. }
  821. /**
  822. * get current access_key
  823. * @param void
  824. * @return string
  825. */
  826. public function get_access_key() {
  827. return $this->access_key;
  828. }
  829. /**
  830. * get current access_secret
  831. * @param void
  832. * @return string
  833. */
  834. public function get_access_secret() {
  835. return $this->access_secret;
  836. }
  837. /**
  838. * set debug switch
  839. * @param bool $flag true/false
  840. * @return $this object
  841. */
  842. public function set_debug($flag) {
  843. $this->debug = ($flag === true);
  844. return $this;
  845. }
  846. /**
  847. * set default bucket
  848. * @param string $name bucket's name
  849. * @param [opt] string $cname bucket's cname
  850. * @return $this object
  851. */
  852. public function set_bucket($name, $cname=null) {
  853. $this->bucket = $name;
  854. if (!empty($cname)) {
  855. $this->bucket_cname = $cname;
  856. }
  857. return $this;
  858. }
  859. /**
  860. * get current bucket
  861. * @param void
  862. * @return string
  863. */
  864. public function get_bucket() {
  865. return $this->bucket;
  866. }
  867. /**
  868. * set default bucket cname
  869. * @param string $cname bucket's cname
  870. * @return $this object
  871. */
  872. public function set_bucket_cname($cname) {
  873. $this->bucket_cname = $cname;
  874. return $this;
  875. }
  876. /**
  877. * get current bucket cname
  878. * @param void
  879. * @return string
  880. */
  881. public function get_bucket_cname() {
  882. return $this->bucket_cname;
  883. }
  884. /**
  885. * Set http request header fields
  886. * @param string $field http header field
  887. * @param string $value value of the field
  888. * usually $field is a string without ":",and $value is not empty,
  889. * example:$filed = "mykey1",$value = "myvalue1";
  890. * meanwhile,$field can be like "key1:value1\nkey2:value2\n..",
  891. * and $value will unused in this situation.
  892. *
  893. * @return $this object
  894. */
  895. public function set_header($field, $value=null) {
  896. $field = trim($field);
  897. $value = trim($value);
  898. if (empty($field)) {
  899. return $this;
  900. }
  901. if (strpos($field, ':')) { //$field can be like "key1:value1\nkey2:value2\n..",$value will unused in this situation
  902. foreach (explode("\n", $field) as $item) {
  903. $key = substr($item, 0, strpos($item, ':'));
  904. $this->headers[$key] = $item;
  905. }
  906. } else {
  907. $this->headers[$field] = "{$field}: {$value}";
  908. }
  909. return $this;
  910. }
  911. /**
  912. * Remove http header field
  913. * @param string $field
  914. * @return $this object
  915. */
  916. public function remove_header($field) {
  917. $field = trim($field);
  918. if (isset($this->headers[$field])) {
  919. unset($this->headers[$field]);
  920. }
  921. return $this;
  922. }
  923. /**
  924. * Set http request body
  925. * @param string $content http request body
  926. * @return $this object
  927. */
  928. public function set_body($content) {
  929. $this->body = $content;
  930. return $this;
  931. }
  932. /**
  933. * Get response code
  934. * @param void
  935. * @return integer
  936. */
  937. public function get_response_code() {
  938. return $this->response_code;
  939. }
  940. /**
  941. * get response header
  942. * @param void
  943. * @return string
  944. */
  945. public function get_response_header() {
  946. return $this->response_header;
  947. }
  948. /**
  949. * Get response content length
  950. * @param void
  951. * @return integer
  952. */
  953. public function get_response_length() {
  954. return $this->response_length;
  955. }
  956. /**
  957. * Get response content
  958. * @param void
  959. * @return string
  960. */
  961. public function get_response_body() {
  962. return $this->response_body;
  963. }
  964. /**
  965. * Get last curl error message
  966. * @param void
  967. * @return string
  968. */
  969. public function get_curl_error() {
  970. return $this->last_curl_error;
  971. }
  972. /**
  973. * Get all buckets,corresponds to "GET Service" in API
  974. * @param void
  975. * @return GCBucket objects list
  976. * @exception see GCError
  977. */
  978. public function get_allbuckets() {
  979. //$conn = $this->make_request('GET', '/');
  980. $conn = $this->make_request_with_path_and_params_split("GET",'/');
  981. $code = $this->exec_request($conn);
  982. if (200 != $code) {
  983. throw new Exception($this->response_body, $code);
  984. }
  985. return $this->parse_bucketsxml($this->response_body);
  986. }
  987. /**
  988. * Get bucket's metas, now only return idc,
  989. * corresponds to "GET Bucket Location" in API
  990. * @param string $bucket bucket's name
  991. * @return string bucket's idc info
  992. * @exception see GCError
  993. */
  994. public function head_bucket($bucket) {
  995. $bucket = trim($bucket, '/');
  996. $bucket = "/{$bucket}?location";
  997. //$conn = $this->make_request('GET', $bucket);
  998. $conn = $this->make_request_with_path_and_params_split("GET",$bucket);
  999. $code = $this->exec_request($conn);
  1000. if (200 != $code) {
  1001. throw new Exception($this->response_body, $code);
  1002. }
  1003. return $this->parse_localxml($this->response_body);
  1004. }
  1005. /**
  1006. * Create new bucket,corresponds to "PUT Bucket" in API
  1007. * @param string $name bucket's name to create
  1008. * @param string $local bucket's region, region of your bucket,
  1009. * region currently support "huadong-1", "huabei-1", default to huabei-1
  1010. * @return true on success
  1011. * @exception see GCError
  1012. */
  1013. public function put_bucket($name, $local='huabei-1') {
  1014. $local_xml = $this->make_bucket_local($local);
  1015. $this->set_header('Content-Length', strlen($local_xml));
  1016. $this->set_body($local_xml);
  1017. //$conn = $this->make_request('PUT', $name, '', 'text/xml');
  1018. $conn = $this->make_request_with_path_and_params_split("PUT",$name,array(),'','text/xml');
  1019. $code = $this->exec_request($conn);
  1020. // code: 204 = success
  1021. if (204 != $code) {
  1022. throw new Exception($this->response_body, $code);
  1023. }
  1024. return true;
  1025. }
  1026. /**
  1027. * Delete specified bucket,corresponds to "Delete Bucket" in API
  1028. * @param string $name bucket's name to delete
  1029. * @return true on success
  1030. * @exception throw exception when bucket is not empty or response invalid
  1031. */
  1032. public function delete_bucket($name) {
  1033. $this->set_header('Content-Length', 0);
  1034. //$conn = $this->make_request('DELETE', $name);
  1035. $conn = $this->make_request_with_path_and_params_split("DELETE",$name);
  1036. $code = $this->exec_request($conn);
  1037. // code: 204 = success
  1038. if (204 != $code) {
  1039. throw new Exception($this->response_body, $code);
  1040. }
  1041. return true;
  1042. }
  1043. /**
  1044. * Get bucket policy,corresponds to "GET Bucket Policy" in API
  1045. * @param string $bucket bucket's name
  1046. * @return string bucket's policy json
  1047. * @exception see GCError
  1048. */
  1049. public function get_bucket_policy($name) {
  1050. $bucket = trim($name,'/');
  1051. $path = "/{$bucket}?policy";
  1052. $conn = $this->make_request_with_path_and_params_split("GET", $path);
  1053. $code = $this->exec_request($conn);
  1054. if (200 != $code) {
  1055. throw new Exception($this->response_body, $code);
  1056. }
  1057. return $this->response_body;
  1058. }
  1059. /**
  1060. * Put bucket policy to storage,corresponds to "PUT Bucket Policy" in API
  1061. * @param string $bucket bucket's name
  1062. * @param array $policy bucket policy config,if $policy is null,delete old bucket policy
  1063. * @return true on success
  1064. * @exception see GCError
  1065. */
  1066. public function put_bucket_policy($bucket, $policy) {
  1067. if($policy === null) { // if policy is null,delete bucket policy
  1068. $is_old_policy = true;
  1069. try {
  1070. $old_policy = $this->get_bucket_policy($bucket);
  1071. } catch (Exception $e) {
  1072. if ($e->getErrorCode() != ERR_BUCKET_NOPOLICY) {
  1073. throw $e;
  1074. }
  1075. $is_old_policy = false;
  1076. }
  1077. if(!$is_old_policy) { // 之前服务器上并不存在bucket policy,直接返回
  1078. return true;
  1079. }
  1080. return $this->delete_bucket_policy($bucket);
  1081. }
  1082. $policy_object = array(
  1083. 'Id' => $this->make_uuid(),
  1084. 'Statement' => $policy
  1085. );
  1086. $stream = fopen('data://text/plain,' . rawurlencode(json_encode($policy_object)), 'rb');
  1087. $code = $this->post_or_put_request("PUT", "{$bucket}?policy", $stream);
  1088. if($code !== 204) {
  1089. throw new Exception($this->response_body,$code);
  1090. }
  1091. return true;
  1092. }
  1093. /**
  1094. * Delete bucket policy,corresponds to "DELETE Bucket Policy" in API
  1095. * @param $bucket bucket's name
  1096. * @return true on success
  1097. * @exception see GCError
  1098. */
  1099. public function delete_bucket_policy($bucket) {
  1100. $path = "{$bucket}?policy";
  1101. return $this->delete_bucket($path);
  1102. }
  1103. /**
  1104. * Get all objects of specified bucket,corresponds to "GET Bucket" in API
  1105. * @param string $bucket bucket's name
  1106. * @param integer $maxkeys max response objects number of per-request
  1107. * @param string $marker response objects offset
  1108. * @param string $delimiter response objects name filter
  1109. * @param string $prefix response objects name filter
  1110. * @return GCEntity object
  1111. * @exception see GCError
  1112. */
  1113. public function get_allobjects($bucket, $maxkeys=null, $marker='', $delimiter='', $prefix='') {
  1114. $bucket = trim($bucket, '/');
  1115. $bucket = "/{$bucket}";
  1116. $params = array();
  1117. if (!empty($maxkeys)) {
  1118. $maxkeys = intval($maxkeys);
  1119. if ($maxkeys > 0) {
  1120. $params['max-keys'] = $maxkeys;
  1121. }
  1122. }
  1123. if ($marker !== '') {
  1124. $params['marker'] = trim($marker);
  1125. }
  1126. if ($delimiter !== '') {
  1127. $params['delimiter'] = trim($delimiter);
  1128. }
  1129. if ($prefix !== '') {
  1130. $params['prefix'] = trim($prefix);
  1131. }
  1132. $conn = $this->make_request_with_path_and_params_split("GET",$bucket,$params);
  1133. $code = $this->exec_request($conn);
  1134. if (200 != $code) {
  1135. throw new Exception($this->response_body, $code);
  1136. }
  1137. return $this->parse_objectsxml($this->response_body);
  1138. }
  1139. /**
  1140. * Get all multipart (corresponds to "List Multipart Upload" in API)
  1141. * @param string $bucket, your bucketname
  1142. * @param string $key_marker,the key to start with
  1143. * @param string $upload_id_marker,the uploadid to start with
  1144. * @param int $max_uploads,the maximum number of keys returned in the response body
  1145. * @param string $prefix,the prefix parameter to the key of the multipart upload you want to retrive
  1146. * @param char $delimiter,the param you use to group keys
  1147. * @return GCMultipartUploadEntity object on success
  1148. * @exception throw exception when response invalid
  1149. */
  1150. public function get_all_multipart_upload($bucket,$key_marker='',$upload_id_marker='', $max_uploads=null, $prefix='', $delimiter='') {
  1151. $bucket = trim($bucket, '/');
  1152. $bucket = "/{$bucket}";
  1153. $params = array();
  1154. if ($max_uploads !== null) {
  1155. $max_uploads = intval($max_uploads);
  1156. if ($max_uploads > 0) {
  1157. $params['max-uploads'] = $max_uploads;
  1158. }
  1159. }
  1160. if ('' !== trim($key_marker)) {
  1161. $params['key-marker'] = trim($key_marker);
  1162. }
  1163. if('' !== trim($upload_id_marker)) {
  1164. $params['upload-id-marker'] = trim($upload_id_marker);
  1165. }
  1166. if ('' !== trim($delimiter)) {
  1167. $params['delimiter'] = trim($delimiter);
  1168. }
  1169. if ('' !== trim($prefix)) {
  1170. $params['prefix'] = trim($prefix);
  1171. }
  1172. $path = $bucket.'?uploads';
  1173. $conn = $this->make_request_with_path_and_params_split('GET',$path,$params);
  1174. $code = $this->exec_request($conn);
  1175. if (200 != $code) {
  1176. throw new Exception($this->response_body, $code);
  1177. }
  1178. return $this->parse_multipart_uploadsxml($this->response_body);
  1179. }
  1180. /**
  1181. * Get object's metas(corresponds to "HEAD Object" in API)
  1182. * @param string $name object's name
  1183. * @return array('name'=>'?', 'meta'=>array(...), 'size'=>?) when success
  1184. * @exception see GCError
  1185. */
  1186. public function head_object($name) {
  1187. $conn = $this->make_request('HEAD', $name);
  1188. $code = $this->exec_request($conn);
  1189. if (200 != $code) {
  1190. throw new Exception($this->response_body, $code);
  1191. }
  1192. $result = $this->parse_header($this->response_header);
  1193. $result['name'] = $name;
  1194. $result['size'] = $this->response_length;
  1195. return $result;
  1196. }
  1197. /**
  1198. * Put object to storage(corresponds to "PUT Object" in API)
  1199. * @param string $name object's name
  1200. * @param string $source local file path(/path/to/filename.ext) or stream
  1201. * @param string $content_meta see make_request()
  1202. * @param string $content_type see make_request()
  1203. * @param string $content_md5 see make_request()
  1204. * @return true on success
  1205. * @exception see GCError
  1206. */
  1207. public function put_object($name, $source, $content_meta='', $content_type='', $content_md5='') {
  1208. if (is_resource($source)) { // stream upload
  1209. if (empty($name)) {
  1210. throw new Exception('$name must be supplied for resource type!', 500);
  1211. }
  1212. fseek($source,0,0);
  1213. }
  1214. elseif (is_string($source)) { // file upload
  1215. if (empty($name)) {
  1216. $name = basename($source);
  1217. }
  1218. }
  1219. if(empty($content_type)) {
  1220. $pathinfo = pathinfo($name);
  1221. $content_type = GCMIME::get_type(isset($pathinfo['extension']) ? $pathinfo['extension'] : '');
  1222. }
  1223. $code = $this->post_or_put_request("PUT", $name, $source,array(),$content_meta,$content_type,$content_md5);
  1224. // code: 204 = success
  1225. if (204 != $code) {
  1226. throw new Exception($this->response_body, $code);
  1227. }
  1228. return true;
  1229. }
  1230. /**
  1231. * Copy Object(corresponds to "PUT Object - Copy" in API)
  1232. * @param $sbucket,name of source bucket
  1233. * @param $skey,name of source object
  1234. * @param $dbucket,name of destnation bucket
  1235. * @param $dkey,name of destnation object
  1236. * @param $content_meta,fileds will be sended as request headers,
  1237. * like x-snda-meta-XXXX or those headers do not necessary
  1238. * return new object info on success
  1239. * @exception see GCError
  1240. */
  1241. public function copy_object($sbucket,$skey,$dbucket,$dkey,$content_meta="",$content_type="") {
  1242. $path = "/{$dbucket}/{$dkey}";
  1243. $copy_source = "/{$sbucket}/{$skey}";
  1244. return $this->copy_from_path_to_path($copy_source,$path,$content_meta,$content_type);
  1245. }
  1246. /**
  1247. * Copy from source path to destnation path, now it used in copy object and upload part copy
  1248. * @param $from_path,source path
  1249. * @param $to_path,destnation path
  1250. * @param $content_meta,fileds will be sended as request headers,
  1251. * like x-snda-meta-XXXX or those headers do not necessary
  1252. * return request info on success
  1253. * @exception see GCError
  1254. */
  1255. public function copy_from_path_to_path($from_path,$to_path,$content_meta="",$content_type="") {
  1256. $content_meta = trim($content_meta);
  1257. if(!empty($content_meta)) {
  1258. $content_meta .= ",";
  1259. }
  1260. $content_meta .= "x-snda-copy-source:{$from_path}";
  1261. $conn = $this->make_request_with_path_and_params_split("PUT",$to_path,array(),$content_meta,$content_type);
  1262. $code = $this->exec_request($conn);
  1263. if($code != 200) {
  1264. throw Exception($this->response_body,$code);
  1265. }
  1266. return $this->parse_copy_object_result($this->response_body);
  1267. }
  1268. /**
  1269. * Get object from storage(corresponds to "GET Object" in API)
  1270. * @param string $name object's name
  1271. * @param string $target write to local file path(/path/to/filename.ext) or stream
  1272. * @param boolean $auto_close if auto close the $target passed when it is a stream?
  1273. * @return true on success
  1274. * @exception see GCError
  1275. */
  1276. public function get_object($name, $target=null, $auto_close=false) {
  1277. $this->head_object($name);//why? ask spring
  1278. //$conn = $this->make_request('GET', $name);
  1279. $conn = $this->make_request_with_path_and_params_split("GET",$name);
  1280. $is_stream = false;
  1281. if ($target !== null) {
  1282. if (is_resource($target)) { // write to stream
  1283. $is_stream = true;
  1284. $target_stream = $target;
  1285. }
  1286. else if (is_string($target)) { // write to local file
  1287. $target_stream = fopen($target, 'wb');
  1288. if (!$target_stream) {
  1289. curl_close($conn);
  1290. throw new Exception("Unable to open {$target}", 500);
  1291. }
  1292. }
  1293. if ($target_stream) {
  1294. curl_setopt_array($conn, array(
  1295. CURLOPT_HEADER => false,
  1296. CURLOPT_FILE => $target_stream
  1297. ));
  1298. }
  1299. }
  1300. $code = $this->exec_request($conn, true);
  1301. if ($auto_close && $is_stream) {
  1302. fclose($target_stream);
  1303. }
  1304. if (200 != $code) {
  1305. throw new Exception($this->response_body, $code);
  1306. }
  1307. return true;
  1308. }
  1309. /**
  1310. * Get object resource from storage
  1311. * @param string $name object's name
  1312. * @param integer $expire expire of resource
  1313. * @return resource on success
  1314. */
  1315. public function get_object_resource($name, $expire=300) {
  1316. $this->head_object($name);
  1317. $path = $this->get_abs_path($name);
  1318. $expire = time() + $expire;
  1319. $auth = "GET\n" // HTTP Method
  1320. ."\n" // Content-MD5 Field
  1321. ."\n" // Content-Type Field
  1322. ."{$expire}\n" // Date Field
  1323. .'' // Canonicalized SNDA Headers
  1324. .$path; // Filepath
  1325. $req_cname = $this->get_bucket_cname();
  1326. $req_params = http_build_query(array(
  1327. 'SNDAAccessKeyId' => $this->get_access_key(),
  1328. 'Expires' => $expire,
  1329. 'Signature' => base64_encode(hash_hmac('sha1', $auth, $this->access_secret, true))
  1330. ));
  1331. return "{$req_cname}{$path}?{$req_params}";
  1332. }
  1333. /**
  1334. * Delete object from storage (corresponds to "Delete Object" in API)
  1335. * @param string $name object's name
  1336. * @return true on success
  1337. * @exception see GCError
  1338. */
  1339. public function delete_object($name) {
  1340. //$conn = $this->make_request('DELETE', $name);
  1341. $conn = $this->make_request_with_path_and_params_split("DELETE",$name);
  1342. $code = $this->exec_request($conn);
  1343. // code: 204 = success
  1344. if (204 != $code) {
  1345. throw new Exception($this->response_body, $code);
  1346. }
  1347. return true;
  1348. }
  1349. /**
  1350. * Initiate multipart upload
  1351. * (corresponds to "Initiate multipart upload" in API)
  1352. * @param string $bucket,bucket's name
  1353. * @param string $key, object's name
  1354. * @param string $content_meta see make_request()
  1355. * @param string $content_type see make_request()
  1356. * @param string $content_md5 see make_request()
  1357. * @return $array,multipart upload info on success
  1358. * @exception throw exception when response invalid
  1359. */
  1360. public function initiate_multipart_upload($bucket, $key, $content_meta='', $content_type='') {
  1361. $bucket = trim($bucket,"/");
  1362. $path = "/{$bucket}/{$key}?uploads";
  1363. if(empty($key)) {
  1364. $pathinfo = pathinfo($key);
  1365. $content_type = GCMIME::get_type(isset($pathinfo['extension']) ? $pathinfo['extension'] : '');
  1366. }
  1367. if(empty($content_type)) {
  1368. $pathinfo = pathinfo($key);
  1369. $content_type = GCMIME::get_type(isset($pathinfo['extension']) ? $pathinfo['extension'] : '');
  1370. }
  1371. //$conn = $this->make_request("POST", $path,$content_meta,$content_type);
  1372. $conn = $this->make_request_with_path_and_params_split("POST",$path,array(),$content_meta,$content_type);
  1373. $code = $this->exec_request($conn);
  1374. if(200 != $code) {
  1375. throw new Exception($this->response_body,$code);
  1376. }
  1377. return $this->parse_initiate_multipart_upload_response($this->response_body);
  1378. }
  1379. /**
  1380. * Upload part to storage
  1381. * @param string $bucketname bucket's name
  1382. * @param string $key object's name
  1383. * @param string $uploadid multipart upload's id
  1384. * @param int $partnumber of this part
  1385. * @param string $source local file path(/path/to/filename.ext) or stream
  1386. * @param long $contentlength the length of this content
  1387. * @param string $content_md5 see make_request()
  1388. * @return true on success
  1389. * @exception throw exception when failed
  1390. */
  1391. public function upload_part($bucketname, $key, $uploadid,$partnumber,$source, $contentlength = null, $content_md5='') {
  1392. if("" === $bucketname || "" === $key || (! is_numeric($partnumber))){
  1393. throw new Exception('Illegal params');
  1394. }
  1395. $params = array(
  1396. "partNumber" => $partnumber,
  1397. "uploadId" => $uploadid
  1398. );
  1399. $path = "/{$bucketname}/{$key}?".http_build_query($params);
  1400. $code = $this->post_or_put_request("PUT", $path, $source,array(),'','',$content_md5,$contentlength);
  1401. if($code != 204) {
  1402. throw new Exception($this->response_body, $code);
  1403. }
  1404. return true;
  1405. }
  1406. /**
  1407. * Upload Part - Copy (corresponds to "Upload Part - Copy" in API)
  1408. * @param $sbucket,name of source bucket
  1409. * @param $skey,name of source object
  1410. * @param $dbucket,name of destnation bucket
  1411. * @param $dkey,name of destnation object
  1412. * @param $uploadid,uploadid of the multipart upload
  1413. * @param $partnumber,partnumber of the part to create
  1414. * @param $content_meta,fileds will be sended as request headers,
  1415. * like x-snda-meta-XXXX or those headers do not necessary
  1416. * return part info on success
  1417. * @exception see GCError
  1418. */
  1419. public function upload_part_copy($sbucket,$skey,$dbucket,$dkey,$uploadid,$partnumber,$content_meta="") {
  1420. $params = array(
  1421. "partNumber" => $partnumber,
  1422. "uploadId" => $uploadid
  1423. );
  1424. $path = "/{$dbucket}/{$dkey}?".http_build_query($params);
  1425. $copy_source = "/{$sbucket}/{$skey}";
  1426. return $this->copy_from_path_to_path($copy_source,$path,$content_meta);
  1427. }
  1428. /**
  1429. * Abort multipart upload
  1430. * @param string $bucket,bucket's name
  1431. * @param string $key,object's name
  1432. * @param string $uploadId, the uploadid of the multipart upload
  1433. * @throws Exception when failed
  1434. * @true on success
  1435. */
  1436. public function abortMultipartUpload($bucket,$key,$uploadId){
  1437. $path = "/{$bucket}/{$key}?uploadId={$uploadId}";
  1438. $conn = $this->make_request_with_path_and_params_split('DELETE', $path);
  1439. $code = $this->exec_request($conn);
  1440. // code: 204 = success
  1441. if (204 != $code) {
  1442. throw new Exception($this->response_body, $code);
  1443. }
  1444. return true;
  1445. }
  1446. /**
  1447. * list parts
  1448. * @param string,$bucket,bucket's name
  1449. * @param string,$key,object's name
  1450. * @param string $uploadId,the uploadid of the multipart upload
  1451. * @param int $max_parts, the maximum number of parts returned in the response body
  1452. * @param string $part_number_marker,the part to start with
  1453. * @return GCPartsEntity on success
  1454. * @exception,throw exception when failed
  1455. */
  1456. public function list_parts($bucket,$key,$uploadId,$max_parts = null,$part_number_marker = '') {
  1457. if("" === $bucket || "" === $key || "" === $uploadId){
  1458. throw new Exception('Illegal params');
  1459. }
  1460. $bucket = trim($bucket);
  1461. $params = array();
  1462. $path = "/{$bucket}/{$key}?uploadId={$uploadId}";
  1463. if(is_numeric($max_parts)) {
  1464. $params["max-parts"] = $max_parts;
  1465. }
  1466. if(is_numeric($part_number_marker)) {
  1467. $params["part-number-marker"] = $part_number_marker;
  1468. }
  1469. $conn = $this->make_request_with_path_and_params_split("GET", $path,$params);
  1470. $code = $this->exec_request($conn);
  1471. if (200 != $code) {
  1472. throw new Exception($this->response_body, $code);
  1473. }
  1474. return $this->parse_listspartxml($this->response_body);
  1475. }
  1476. /**
  1477. * complete multipartupload
  1478. * @param string $bucket
  1479. * @param string $key
  1480. * @param string $uploadid
  1481. * @param string $complete_xml,if is '',then we will get it by list_parts($uploadid)
  1482. * @throws Exception when failed
  1483. * @return response body
  1484. */
  1485. public function complete_multipartupload($bucket,$key,$uploadid,$complete_xml = ''){
  1486. if(empty($complete_xml)) {
  1487. try {
  1488. $partEntity = $this->list_parts($bucket, $key, $uploadid);
  1489. $complete_xml = $partEntity->to_completemultipartuploadxml();
  1490. } catch(Exception $e) {
  1491. throw $e;
  1492. }
  1493. }
  1494. $path = "/{$bucket}/{$key}?uploadId={$uploadid}";
  1495. $stream = fopen('data://text/plain,' . rawurlencode($complete_xml), 'rb');
  1496. $code = $this->post_or_put_request("POST",$path,$stream);
  1497. if ( 300 <= $code) {
  1498. throw new Exception($this->response_body, $code);
  1499. }
  1500. return $this->parse_complete_multipart_uploadxml($this->response_body);
  1501. }
  1502. /**
  1503. * get resource abs path
  1504. * @param string $path
  1505. * @return string
  1506. */
  1507. public function get_abs_path($path) {
  1508. if ('/' != $path[0]) {
  1509. $path = $this->bucket ? "/{$this->bucket}/{$path}" : "/{$path}";
  1510. }
  1511. $path = preg_replace('~/+~', '/', $path);
  1512. return $path;
  1513. }
  1514. /**
  1515. * Execute curl request
  1516. * @param resource $ch curl handle
  1517. * @param bool $close_request whether call curl_close() after execute request
  1518. * @return http success status code or false
  1519. * @exception throw GCError when response code in 400~499 range
  1520. */
  1521. public function exec_request($ch, $close_request=true) {
  1522. if (!is_resource($ch)) {
  1523. return false;
  1524. }
  1525. $response = curl_exec($ch);
  1526. $this->last_curl_error = curl_error($ch);
  1527. if (!empty($this->last_curl_error)) {
  1528. throw new Exception($this->last_curl_error,0);
  1529. }
  1530. $this->response_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
  1531. $this->response_length = curl_getinfo($ch, CURLINFO_CONTENT_LENGTH_DOWNLOAD);
  1532. $tmparray = explode("\r\n\r\n", $response);
  1533. if (isset($tmparray[1])) {
  1534. $this->response_header = array_shift($tmparray);
  1535. $this->response_body = implode("\r\n\r\n", $tmparray);
  1536. } else {
  1537. $this->response_body = $response;
  1538. }
  1539. if ($close_request) {
  1540. curl_close($ch);
  1541. }
  1542. if ($this->response_code >= 400 && $this->response_code <= 499) {
  1543. throw new GCError($this->response_code, $this->response_body);
  1544. }
  1545. return $this->response_code;
  1546. }
  1547. public function parse_header($header) {
  1548. $tmparray = explode("\r\n", $header);
  1549. $result = array();
  1550. $others = array();
  1551. foreach ($tmparray as $item) {
  1552. $item = trim($item);
  1553. if ('x-snda-meta-' === substr($item, 0, 12)) {
  1554. $tmpitem = explode(':', $item);
  1555. if (isset($tmpitem[1])) {
  1556. $result[substr(trim($tmpitem[0]), 12)] = trim($tmpitem[1]);
  1557. } else {
  1558. $result[substr($item, 12)] = null;
  1559. }
  1560. } else if('HTTP' === substr($item,0,strlen('HTTP'))) {
  1561. continue;
  1562. }else {
  1563. $tmpitem = explode(':', $item);
  1564. if(isset($tmpitem[1])) {
  1565. $others[trim($tmpitem[0])] = trim($tmpitem[1]);
  1566. } else {
  1567. $others[trim($tmpitem[0])] = null;
  1568. }
  1569. }
  1570. }
  1571. $others['meta'] = $result;
  1572. return $others;
  1573. }
  1574. public function parse_bucketsxml($bucketsxml) {
  1575. $bucketsxml = $this->get_xmlpart($bucketsxml);
  1576. $doc = new DOMDocument();
  1577. $doc->loadXML($bucketsxml);
  1578. $buckets = $doc->getElementsByTagName('Bucket');
  1579. $bucketsarray = array();
  1580. foreach($buckets as $xml) {
  1581. $idc = self::DEFAULT_LOCATION;
  1582. $name = $xml->getElementsByTagName('Name')->item(0)->nodeValue;
  1583. $ctime = $xml->getElementsByTagName('CreationDate')->item(0)->nodeValue;
  1584. $local = $xml->getElementsByTagName('Location')->item(0);
  1585. if ($local && !empty($local->nodeValue)) {
  1586. $idc = $local->nodeValue;
  1587. }
  1588. $bucketsarray[] = new GCBucket($idc, $name, $ctime);
  1589. }
  1590. return $bucketsarray;
  1591. }
  1592. public function parse_localxml($idcxml) {
  1593. $idcxml = $this->get_xmlpart($idcxml);
  1594. $doc = new DOMDocument();
  1595. $doc->loadXML($idcxml);
  1596. $local = $doc->getElementsByTagName('LocationConstraint')->item(0);
  1597. if (empty($local)) {
  1598. return self::DEFAULT_LOCATION;
  1599. }
  1600. if (!empty($local->nodeValue)) {
  1601. return $local->nodeValue;
  1602. }
  1603. return self::DEFAULT_LOCATION;
  1604. }
  1605. protected function parse_copy_object_result($body) {
  1606. $body_xml = $this->get_xmlpart($body);
  1607. $doc = new DOMDocument();
  1608. $doc->loadXML($body_xml);
  1609. $message = $doc->getElementsByTagName("CopyObjectResult");
  1610. if(empty($message)) {
  1611. throw new GCError($body,200);
  1612. }
  1613. try{
  1614. $result = array(
  1615. "LastModified" => $doc->getElementsByTagName("LastModified")->item(0)->nodeValue,
  1616. "ETag" => $doc->getElementsByTagName("ETag")->item(0)->nodeValue);
  1617. return $result;
  1618. }catch(Exception $e ) {
  1619. throw new Exception($body,200);
  1620. }
  1621. }
  1622. public function parse_objectsxml($objectxml) {
  1623. $objectxml = $this->get_xmlpart($objectxml);
  1624. $doc = new DOMDocument();
  1625. $doc->loadXML($objectxml);
  1626. $xpath = new DOMXPath($doc);
  1627. $entity = new GCEntity();
  1628. $name = $xpath->query('/ListBucketResult/Name')->item(0);
  1629. if ($name) {
  1630. $entity->set_bucket($name->nodeValue);
  1631. }
  1632. $maxkeys = $xpath->query('/ListBucketResult/MaxKeys')->item(0);
  1633. if ($maxkeys) {
  1634. $entity->set_maxkeys($maxkeys->nodeValue);
  1635. }
  1636. $istruncated = $xpath->query('/ListBucketResult/IsTruncated')->item(0);
  1637. if ($istruncated) {
  1638. $entity->set_istruncated($istruncated->nodeValue === 'true');
  1639. }
  1640. $prefix = $xpath->query('/ListBucketResult/Prefix')->item(0);
  1641. if ($prefix) {
  1642. $entity->set_prefix($prefix->nodeValue);
  1643. }
  1644. $delimiter = $xpath->query('/ListBucketResult/Delimiter')->item(0);
  1645. if ($delimiter) {
  1646. $entity->set_delimiter($delimiter->nodeValue);
  1647. }
  1648. $marker = $xpath->query('/ListBucketResult/NextMarker')->item(0);
  1649. if ($marker) {
  1650. $entity->set_marker($marker->nodeValue);
  1651. }
  1652. $objects = $xpath->query('/ListBucketResult/Contents');
  1653. foreach($objects as $xml) {
  1654. $key = $xml->getElementsByTagName('Key')->item(0)->nodeValue;
  1655. $size = $xml->getElementsByTagName('Size')->item(0)->nodeValue;
  1656. $lastmodified = $xml->getElementsByTagName('LastModified')->item(0)->nodeValue;
  1657. $etag = $xml->getElementsByTagName('ETag')->item(0)->nodeValue;
  1658. $entity->add_object(new GCObject($key, $size, $lastmodified, $etag));
  1659. }
  1660. $common = $xpath->query('/ListBucketResult/CommonPrefixes');
  1661. foreach ($common as $comxml) {
  1662. $folders = $comxml->getElementsByTagName('Prefix');
  1663. foreach ($folders as $xml) {
  1664. $entity->add_object(new GCObject($xml->nodeValue, '-', '-', ''));
  1665. }
  1666. }
  1667. // adjust marker when no delimiter
  1668. if ($marker === null && $entity->get_istruncated()) {
  1669. $last_object = $entity->get_object(-1);
  1670. if(empty($last_object) === false) {
  1671. $entity->set_marker($last_object->get_key());
  1672. }
  1673. }
  1674. return $entity;
  1675. }
  1676. protected function parse_initiate_multipart_upload_response($response_xml) {
  1677. $response_xml = $this->get_xmlpart($response_xml);
  1678. $doc = new DOMDocument();
  1679. $doc->loadXML($response_xml);
  1680. $multipart_upload_tree = $doc->getElementsByTagName(GCMultipartUpload::$InitiateMultipartUploadResultTag)->item(0);
  1681. if($multipart_upload_tree->childNodes->length) {
  1682. $bucket = $multipart_upload_tree->getElementsByTagName(GCMultipartUpload::$bucketTag)->item(0)->nodeValue;
  1683. $key = $multipart_upload_tree->getElementsByTagName(GCMultipartUpload::$keyTag)->item(0)->nodeValue;
  1684. $value = $multipart_upload_tree -> getElementsByTagName(GCMultipartUpload::$uploadIdTag)->item(0)->nodeValue;
  1685. $result = new GCMultipartUpload($bucket,$key,$value);
  1686. return $result->to_array();
  1687. }
  1688. return false;
  1689. }
  1690. protected function parse_multipart_uploadsxml($mulpart_uploads_xml) {
  1691. $mulpart_uploads_xml = $this->get_xmlpart($mulpart_uploads_xml);
  1692. $doc = new DOMDocument();
  1693. $doc->loadXML($mulpart_uploads_xml);
  1694. $xpath = new DOMXPath($doc);
  1695. $entity = new GCMultipartUploadEntity();
  1696. $bucketTag = '/'.GCMultipartUploadEntity::$listMultipartUploadsResultTag.'/'.GCMultipartUploadEntity::$bucketTag;
  1697. $bucket = $xpath->query($bucketTag)->item(0);
  1698. if ($bucket) {
  1699. $entity->set_bucket($bucket->nodeValue);
  1700. }
  1701. $maxkeysTag = '/'.GCMultipartUploadEntity::$listMultipartUploadsResultTag.'/'.GCMultipartUploadEntity::$maxUploadsTag;
  1702. $maxkeys = $xpath->query($maxkeysTag)->item(0);
  1703. if ($maxkeys) {
  1704. $entity->set_maxUploads($maxkeys->nodeValue);
  1705. }
  1706. $istruncatedTag = '/'.GCMultipartUploadEntity::$listMultipartUploadsResultTag.'/'.GCMultipartUploadEntity::$isTruncatedTag;
  1707. $istruncated = $xpath->query($istruncatedTag)->item(0);
  1708. if ($istruncated) {
  1709. $entity->set_istruncated($istruncated->nodeValue === 'true');
  1710. }
  1711. $prefixTag = '/'.GCMultipartUploadEntity::$listMultipartUploadsResultTag.'/'.GCMultipartUploadEntity::$prefixTag;
  1712. $prefix = $xpath->query($prefixTag)->item(0);
  1713. if ($prefix) {
  1714. $entity->set_prefix($prefix->nodeValue);
  1715. }
  1716. $delimiterTag = '/'.GCMultipartUploadEntity::$listMultipartUploadsResultTag.'/'.GCMultipartUploadEntity::$delimiterTag;
  1717. $delimiter = $xpath->query($delimiterTag)->item(0);
  1718. if ($delimiter) {
  1719. $entity->set_delimiter($delimiter->nodeValue);
  1720. }
  1721. $keymarkerTag = '/'.GCMultipartUploadEntity::$listMultipartUploadsResultTag.'/'.GCMultipartUploadEntity::$keyMarkerTag;
  1722. $marker = $xpath->query($keymarkerTag)->item(0);
  1723. if ($marker) {
  1724. $entity->set_keyMarker($marker->nodeValue);
  1725. }
  1726. $uploadidmarkerTag = '/'.GCMultipartUploadEntity::$listMultipartUploadsResultTag.'/'.GCMultipartUploadEntity::$uploadIdMarker;
  1727. $uploadidmarker = $xpath->query($uploadidmarkerTag)->item(0);
  1728. if($uploadidmarker) {
  1729. $entity->set_uploadIdMarker($uploadidmarker->nodeValue);
  1730. }
  1731. $nextkeymarkerTag = '/'.GCMultipartUploadEntity::$listMultipartUploadsResultTag.'/'.GCMultipartUploadEntity::$nextKeyMarkerTag;
  1732. $nextkeymarker = $xpath->query($nextkeymarkerTag)->item(0);
  1733. if($nextkeymarker) {
  1734. $entity -> set_nextKeyMarker($nextkeymarker->nodeValue);
  1735. }
  1736. $nextuploadidmarkerTag = '/'.GCMultipartUploadEntity::$listMultipartUploadsResultTag.'/'.GCMultipartUploadEntity::$nextUploadIdMarkerTag;
  1737. $nextuploadidmarker = $xpath -> query($nextuploadidmarkerTag) -> item(0);
  1738. if($nextuploadidmarker) {
  1739. $entity -> set_nextUploadIdMarker($nextuploadidmarker->nodeValue);
  1740. }
  1741. $uploadTag = '/'.GCMultipartUploadEntity::$listMultipartUploadsResultTag.'/'.GCMultipartUploadEntity::$uploadTag;
  1742. $uploads = $xpath->query($uploadTag);
  1743. foreach($uploads as $xml) {
  1744. $key = $xml->getElementsByTagName(GCMultipartUpload::$keyTag)->item(0)->nodeValue;
  1745. $uploadid = $xml->getElementsByTagName(GCMultipartUpload::$uploadIdTag)->item(0)->nodeValue;
  1746. $lastmodified = $xml->getElementsByTagName(GCMultipartUpload::$initiatedTag)->item(0)->nodeValue;
  1747. $entity->addUpload(new GCMultipartUpload($bucket->nodeValue,$key,$uploadid,$lastmodified));
  1748. }
  1749. $commonPrefixTag = '/'.GCMultipartUploadEntity::$listMultipartUploadsResultTag.'/'.GCMultipartUploadEntity::$commonPrefixesTag;
  1750. $common = $xpath->query($commonPrefixTag);
  1751. foreach ($common as $comxml) {
  1752. $folders = $comxml->getElementsByTagName(GCMultipartUploadEntity::$prefixTag);
  1753. foreach ($folders as $xml) {
  1754. $entity->add_object(new GCMultipartUpload($bucket->nodeValue, $xml->nodeValue, '-', '-'));
  1755. }
  1756. }
  1757. return $entity;
  1758. }
  1759. protected function parse_listspartxml($listparts_xml) {
  1760. $listparts_xml = $this->get_xmlpart($listparts_xml);
  1761. $doc = new DOMDocument();
  1762. $doc->loadXML($listparts_xml);
  1763. $xpath = new DOMXPath($doc);
  1764. $entity = new GCPartsEntity();
  1765. $bucketTag = '/'.GCPartsEntity::$listpartsresultTag.'/'.GCPartsEntity::$bucketTag;
  1766. $bucket = $xpath->query($bucketTag)->item(0);
  1767. if ($bucket) {
  1768. $entity->set_bucket($bucket->nodeValue);
  1769. }
  1770. $keyTag = '/'.GCPartsEntity::$listpartsresultTag.'/'.GCPartsEntity::$keyTag;
  1771. $key = $xpath->query($keyTag)->item(0);
  1772. if ($key) {
  1773. $entity->set_key($key->nodeValue);
  1774. }
  1775. $uploadidTag = '/'.GCPartsEntity::$listpartsresultTag.'/'.GCPartsEntity::$uploadIdTag;
  1776. $uploadid = $xpath->query($uploadidTag)->item(0);
  1777. if($uploadid) {
  1778. $entity->set_uploadid($uploadid->nodeValue);
  1779. }
  1780. $maxpartsTag = '/'.GCPartsEntity::$listpartsresultTag.'/'.GCPartsEntity::$maxpartsTag;
  1781. $maxparts = $xpath->query($maxpartsTag)->item(0);
  1782. if($maxparts) {
  1783. $entity->set_maxparts($maxparts->nodeType);
  1784. }
  1785. $istruncatedTag = '/'.GCPartsEntity::$listpartsresultTag.'/'.GCPartsEntity::$istruncatedTag;
  1786. $istruncated = $xpath->query($istruncatedTag)->item(0);
  1787. if ($istruncated) {
  1788. $entity->set_istruncated($istruncated->nodeValue === 'true');
  1789. }
  1790. $partnumbermarkerTag = '/'.GCPartsEntity::$listpartsresultTag.'/'.GCPartsEntity::$partnumberMarkerTag;
  1791. $partnumbermarker = $xpath->query($partnumbermarkerTag)->item(0);
  1792. if($partnumbermarker) {
  1793. $entity->set_partnumbermarker($partnumbermarker->nodeValue);
  1794. }
  1795. $nextpartnumbermarkerTag = '/'.GCPartsEntity::$listpartsresultTag.'/'.GCPartsEntity::$nextpartnumbermarkerTag;
  1796. $nextpartnumbermarker = $xpath->query($nextpartnumbermarkerTag)->item(0);
  1797. if($nextpartnumbermarker) {
  1798. $entity->set_nextpartnumbermarker($nextpartnumbermarker->nodeValue);
  1799. }
  1800. $partsTag = '/'.GCPartsEntity::$listpartsresultTag.'/'.GCPartsEntity::$partTag;
  1801. $parts = $xpath->query($partsTag);
  1802. foreach($parts as $xml) {
  1803. $partnumber = $xml->getElementsByTagName(GCUploadPart::$partnumberTag)->item(0)->nodeValue;
  1804. $etag = $xml->getElementsByTagName(GCUploadPart::$etagTag)->item(0)->nodeValue;
  1805. $lastmodified = $xml->getElementsByTagName(GCUploadPart::$lastModifiedTag)->item(0)->nodeValue;
  1806. $size = $xml->getElementsByTagName(GCUploadPart::$sizeTag)->item(0)->nodeValue;
  1807. $entity->add_part(new GCUploadPart($key->nodeValue,$size,$lastmodified,$etag,$partnumber));
  1808. }
  1809. return $entity;
  1810. }
  1811. protected function parse_complete_multipart_uploadxml($xml) {
  1812. $xml = $this->get_xmlpart($xml);
  1813. $completeMultipartUploadResultTag = "CompleteMultipartUploadResult";
  1814. $locationTag = "/".$completeMultipartUploadResultTag."/Location";
  1815. $bucketTag = "/".$completeMultipartUploadResultTag."/Bucket";
  1816. $keyTag = "/".$completeMultipartUploadResultTag."/Key";
  1817. $eTagTag = "/".$completeMultipartUploadResultTag."/ETag";
  1818. $doc = new DOMDocument();
  1819. $doc->loadXML($xml);
  1820. $result = array();
  1821. $xpath = new DOMXPath($doc);
  1822. $location = $xpath->query($locationTag)->item(0);
  1823. if($location) {
  1824. $result["Location"] = $location->nodeValue;
  1825. }
  1826. $bucket = $xpath->query($bucketTag)->item(0);
  1827. if($bucket) {
  1828. $result["Bucket"] = $bucket->nodeValue;
  1829. }
  1830. $key = $xpath->query($keyTag)->item(0);
  1831. if($key) {
  1832. $result["Key"] = $key->nodeValue;
  1833. }
  1834. $eTag = $xpath->query($eTagTag)->item(0);
  1835. if($eTag) {
  1836. $result["ETag"] = $eTag->nodeValue;
  1837. }
  1838. return $result;
  1839. }
  1840. /**
  1841. * Get xml part from response body
  1842. */
  1843. protected function get_xmlpart($response_body) {
  1844. $tmparray = explode("\r\n\r\n", $response_body);
  1845. $realbody = array();
  1846. for($i=0;$i<count($tmparray);$i++) {
  1847. $tmp = trim($tmparray[$i]);
  1848. //printf("\nvc".substr($tmp,0,strlen("<?xml"))."\n");
  1849. if(substr($tmp,0,strlen("<?xml")) === "<?xml") {
  1850. break;
  1851. }
  1852. }
  1853. for(;$i<count($tmparray);$i++) {
  1854. $realbody[]=$tmparray[$i];
  1855. }
  1856. $realxml = implode("\r\n\r\n",$realbody);
  1857. // printf("realxml:\n".$realxml."\n");
  1858. return $realxml;
  1859. }
  1860. /**
  1861. * generate uuid string
  1862. * @param string $prefix
  1863. * @return string
  1864. */
  1865. protected function make_uuid($prefix='') {
  1866. $chars = md5(uniqid(mt_rand(), true));
  1867. $uuid = substr($chars,0,8) . '-';
  1868. $uuid .= substr($chars,8,4) . '-';
  1869. $uuid .= substr($chars,12,4) . '-';
  1870. $uuid .= substr($chars,16,4) . '-';
  1871. $uuid .= substr($chars,20,12);
  1872. return $prefix . $uuid;
  1873. }
  1874. /**
  1875. * sign the data
  1876. * @param string $data
  1877. * @return string
  1878. */
  1879. protected function make_sign($data) {
  1880. return 'SNDA'.' '.$this->access_key.':'.base64_encode(hash_hmac('sha1', $data, $this->access_secret, true));
  1881. }
  1882. /**
  1883. * adjust the meta
  1884. * @param string $meta
  1885. * @return string
  1886. */
  1887. protected function make_meta($meta) {
  1888. /**
  1889. * compress
  1890. * x-snda-meta-row: abc, x-snda-meta-row: bcd
  1891. * to
  1892. * x-snda-meta-row:abc,bcd // value have no lead space
  1893. */
  1894. $tmparray = array();
  1895. foreach (explode(',', trim($meta)) as $item) {
  1896. $item = explode(':', $item);
  1897. if (isset($item[1])) {
  1898. $tmparray[trim($item[0])][] = trim($item[1]);
  1899. }
  1900. }
  1901. $keys = array_keys($tmparray);
  1902. sort($keys);
  1903. $meta = '';
  1904. foreach ($keys as $key) {
  1905. $meta .= "{$key}:".join(',', $tmparray[$key])."\n";
  1906. }
  1907. return $meta;
  1908. }
  1909. /**
  1910. * Generate request handler
  1911. * @param string $method GET, HEAD, PUT, DELETE
  1912. * @param string $path resource $path,used in sign
  1913. * @param array $params $query params
  1914. * @param string $content_meta x-snda-meta-XXXX field
  1915. * @param string $content_type Content-Type field
  1916. * @param string $content_md5 Content-MD5 field
  1917. * @return cURL handle on success, false if any error.
  1918. */
  1919. protected function make_request_with_path_and_params_split($method, $path, $query_params = array(),$content_meta='', $content_type='', $content_md5='') {
  1920. $path = $this->get_abs_path($path);
  1921. if ($content_meta) {
  1922. $content_meta = $this->make_meta($content_meta);
  1923. $this->set_header($content_meta);
  1924. }
  1925. if ($content_type) {
  1926. $this->set_header('Content-Type', $content_type);
  1927. }
  1928. if ($content_md5) {
  1929. $this->set_header('Content-MD5', base64_encode($content_md5));
  1930. }
  1931. $conn = curl_init();
  1932. if ($conn) {
  1933. $url = "{$this->host}{$path}";
  1934. if(!empty($query_params)) {
  1935. $params_str = http_build_query($query_params);
  1936. if(false === strstr($path, '?')) {
  1937. $url .= "?";
  1938. } else {
  1939. $url .= "&";
  1940. }
  1941. $url .= $params_str;
  1942. }
  1943. $date = date('r');
  1944. $auth = "{$method}\n" // HTTP Method
  1945. ."{$content_md5}\n" // Content-MD5 Field
  1946. ."{$content_type}\n" // Content-Type Field
  1947. ."{$date}\n" // Date Field
  1948. .$content_meta // Canonicalized SNDA Headers
  1949. .$path; // resource path
  1950. //print_r("string to sign:".$auth."\n");
  1951. $this->set_header('Date', $date);
  1952. $this->set_header('Authorization', $this->make_sign($auth));
  1953. $this->set_header('Expect', '');
  1954. curl_setopt_array($conn, array(
  1955. CURLOPT_URL => $url,
  1956. CURLOPT_VERBOSE => $this->debug,
  1957. CURLOPT_CUSTOMREQUEST => $method,
  1958. CURLOPT_CONNECTTIMEOUT => 10,
  1959. CURLOPT_FOLLOWLOCATION => true,
  1960. CURLOPT_HEADER => true,
  1961. CURLOPT_NOBODY => 'HEAD' === $method,
  1962. CURLOPT_RETURNTRANSFER => true,
  1963. CURLOPT_BINARYTRANSFER => true,
  1964. CURLOPT_HTTPHEADER => $this->headers
  1965. ));
  1966. if (strstr($this->host, ':')) {
  1967. $tmparray = explode(':', $this->host);
  1968. if (isset($tmparray[2])) {
  1969. curl_setopt($conn, CURLOPT_PORT, intval($tmparray[2]));
  1970. }
  1971. }
  1972. if (!empty($this->body)) {
  1973. if (is_array($this->body)) {
  1974. $this->body = http_build_query($this->body);
  1975. }
  1976. curl_setopt_array($conn, array(
  1977. CURLOPT_POST => 1,
  1978. CURLOPT_POSTFIELDS => $this->body
  1979. ));
  1980. }
  1981. $this->body = null;
  1982. $this->headers = array();
  1983. } else {
  1984. throw new Exception('Failed to init curl, maybe it is not supported yet?');
  1985. }
  1986. return $conn;
  1987. }
  1988. /**
  1989. * generate request handler
  1990. * @param string $method GET, HEAD, PUT, DELETE
  1991. * @param string $path resource $path
  1992. * @param string $content_meta fileds will be sended as request headers,
  1993. * like x-snda-meta-XXXX or those headers do not necessary
  1994. * @param string $content_type Content-Type field
  1995. * @param string $content_md5 Content-MD5 field
  1996. * @return cURL handle on success, false if any error.
  1997. */
  1998. protected function make_request($method, $path,$content_meta='', $content_type='', $content_md5='') {
  1999. //$path = $this->get_abs_path($path);
  2000. $params = array();
  2001. if (strstr($path, '?')) {
  2002. $tmparray = explode('?', $path);
  2003. $path = array_shift($tmparray);
  2004. $query_string = implode('?', $tmparray);
  2005. parse_str($query_string,$params);
  2006. }
  2007. $params_array = array();
  2008. if (!empty($params)) {
  2009. $isfirst = true;
  2010. foreach ($params as $key=>$value) {
  2011. if($value === NULL || $value === '') {
  2012. if(true === $isfirst) {
  2013. $path .= "?".$key; // may have bug?
  2014. $isfirst = false;
  2015. }
  2016. } else {
  2017. $params_array[$key] = $value;
  2018. }
  2019. }
  2020. }
  2021. return $this->make_request_with_path_and_params_split($method, $path, $params_array,$content_meta, $content_type, $content_md5);
  2022. }
  2023. /**
  2024. * process post or put request
  2025. * @param string $path,path need to sign
  2026. * @param resource $source,source data to post
  2027. * @param array $query_params,params in query
  2028. * @param string $content_meta,fileds will be sended as request headers,
  2029. * like x-snda-meta-XXXX or those headers do not necessary.
  2030. * @param string $content_type
  2031. * @param string $content_md5
  2032. * @throws Exception
  2033. * @return code;
  2034. */
  2035. protected function post_or_put_request($method,$path, $source, $query_params=array(),$content_meta='', $content_type='', $content_md5='',$content_length ='') {
  2036. if (is_resource($source)) { // stream upload
  2037. $source_stream = $source;
  2038. $source_fstat = fstat($source);
  2039. $source_size = isset($source_fstat['size']) ? $source_fstat['size'] : 0;
  2040. }
  2041. elseif (is_string($source)) { // file upload
  2042. clearstatcache();
  2043. if (!is_file($source)) {
  2044. throw new Exception("{$source} doesn't exist", 404);
  2045. }
  2046. $source_stream = fopen($source, 'rb');
  2047. if (!$source_stream) {
  2048. throw new Exception("Unable to read {$source}", 500);
  2049. }
  2050. $source_size = filesize($source);
  2051. }
  2052. elseif ($source === null) { // no content
  2053. $source_stream = null;
  2054. $source_size = 0;
  2055. } else {
  2056. throw new Exception('Unsupported source type!', 500);
  2057. }
  2058. if(is_numeric($content_length)) { //if $content_length is set,check weather $content_length is illegal
  2059. $content_length = intval($content_length);
  2060. if($content_length > $source_size || $content_length < 0) {
  2061. throw new Exception("Content_length({$content_length}) is illegal",500);
  2062. } else {
  2063. $source_size = $content_length;
  2064. }
  2065. }
  2066. $code = 200;
  2067. try {
  2068. if ($source_size === 0) {
  2069. $this->set_header('content-length', $source_size);
  2070. }
  2071. $conn = $this->make_request_with_path_and_params_split($method, $path,$query_params,$content_meta, $content_type, $content_md5);
  2072. if ($source_size !== 0) {
  2073. curl_setopt_array($conn, array(
  2074. CURLOPT_PUT => true,
  2075. CURLOPT_INFILE => $source_stream,
  2076. CURLOPT_INFILESIZE => $source_size
  2077. ));
  2078. }
  2079. $code = $this->exec_request($conn);
  2080. if (is_resource($source_stream)) {
  2081. fclose($source_stream);
  2082. }
  2083. } catch (Exception $e){
  2084. if (is_resource($source_stream)) {
  2085. fclose($source_stream);
  2086. }
  2087. throw $e;
  2088. }
  2089. return $code;
  2090. }
  2091. /**
  2092. * build bucket local xml
  2093. * @param string $local
  2094. * @return string
  2095. */
  2096. protected function make_bucket_local($local) {
  2097. $local = strtolower($local);
  2098. $template = '<?xml version="1.0" encoding="UTF-8"?><CreateBucketConfiguration><LocationConstraint>%s</LocationConstraint></CreateBucketConfiguration>';
  2099. //若用户选择huadong-1,则默认不传location body
  2100. $local_xml = '';
  2101. $local = trim($local);
  2102. switch ($local) {
  2103. case 'huadong-1':
  2104. break;
  2105. default:
  2106. $local_xml = sprintf($template,$local);
  2107. break;
  2108. }
  2109. return $local_xml;
  2110. }
  2111. }