OssUtil.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  1. <?php
  2. namespace OSS\Core;
  3. /**
  4. * Class OssUtil
  5. *
  6. * Oss Util class for OssClient. The caller could use it for formating the result from OssClient.
  7. *
  8. * @package OSS
  9. */
  10. class OssUtil
  11. {
  12. const OSS_CONTENT = 'content';
  13. const OSS_LENGTH = 'length';
  14. const OSS_HEADERS = 'headers';
  15. const OSS_MAX_OBJECT_GROUP_VALUE = 1000;
  16. const OSS_MAX_PART_SIZE = 5368709120; // 5GB
  17. const OSS_MID_PART_SIZE = 10485760; // 10MB
  18. const OSS_MIN_PART_SIZE = 102400; // 100KB
  19. /**
  20. * Generate query params
  21. *
  22. * @param array $options: a key-value pair array.
  23. * @return string: the key-value list in the format such as key1=value1&key2=value2
  24. */
  25. public static function toQueryString($options = array())
  26. {
  27. $temp = array();
  28. uksort($options, 'strnatcasecmp');
  29. foreach ($options as $key => $value) {
  30. if (is_string($key) && !is_array($value)) {
  31. $temp[] = rawurlencode($key) . '=' . rawurlencode($value);
  32. }
  33. }
  34. return implode('&', $temp);
  35. }
  36. /**
  37. * Html encoding '<', '>', '&', '\', '"' in subject parameter.
  38. *
  39. * @param string $subject
  40. * @return string
  41. */
  42. public static function sReplace($subject)
  43. {
  44. $search = array('<', '>', '&', '\'', '"');
  45. $replace = array('&lt;', '&gt;', '&amp;', '&apos;', '&quot;');
  46. return str_replace($search, $replace, $subject);
  47. }
  48. /**
  49. * Check whether the string includes any chinese character
  50. *
  51. * @param $str
  52. * @return int
  53. */
  54. public static function chkChinese($str)
  55. {
  56. return preg_match('/[\x80-\xff]./', $str);
  57. }
  58. /**
  59. * Checks if the string is encoded by GB2312.
  60. *
  61. * @param string $str
  62. * @return boolean false UTF-8 encoding TRUE GB2312 encoding
  63. */
  64. public static function isGb2312($str)
  65. {
  66. for ($i = 0; $i < strlen($str); $i++) {
  67. $v = ord($str[$i]);
  68. if ($v > 127) {
  69. if (($v >= 228) && ($v <= 233)) {
  70. if (($i + 2) >= (strlen($str) - 1)) return true; // not enough characters
  71. $v1 = ord($str[$i + 1]);
  72. $v2 = ord($str[$i + 2]);
  73. if (($v1 >= 128) && ($v1 <= 191) && ($v2 >= 128) && ($v2 <= 191))
  74. return false;
  75. else
  76. return true;
  77. }
  78. }
  79. }
  80. return false;
  81. }
  82. /**
  83. * Checks if the string is encoded by GBK
  84. *
  85. * @param string $str
  86. * @param boolean $gbk
  87. * @return boolean
  88. */
  89. public static function checkChar($str, $gbk = true)
  90. {
  91. for ($i = 0; $i < strlen($str); $i++) {
  92. $v = ord($str[$i]);
  93. if ($v > 127) {
  94. if (($v >= 228) && ($v <= 233)) {
  95. if (($i + 2) >= (strlen($str) - 1)) return $gbk ? true : FALSE; // not enough characters
  96. $v1 = ord($str[$i + 1]);
  97. $v2 = ord($str[$i + 2]);
  98. if ($gbk) {
  99. return (($v1 >= 128) && ($v1 <= 191) && ($v2 >= 128) && ($v2 <= 191)) ? FALSE : TRUE;//GBK
  100. } else {
  101. return (($v1 >= 128) && ($v1 <= 191) && ($v2 >= 128) && ($v2 <= 191)) ? TRUE : FALSE;
  102. }
  103. }
  104. }
  105. }
  106. return $gbk ? TRUE : FALSE;
  107. }
  108. /**
  109. * Checks if the bucket name is valid
  110. * bucket naming rules
  111. * 1. Can only include lowercase letters, numbers, or dashes
  112. * 2. Must start and end with lowercase letters or numbers
  113. * 3. Must be within a length from 3 to 63 bytes.
  114. *
  115. * @param string $bucket Bucket name
  116. * @return boolean
  117. */
  118. public static function validateBucket($bucket)
  119. {
  120. $pattern = '/^[a-z0-9][a-z0-9-]{2,62}$/';
  121. if (!preg_match($pattern, $bucket)) {
  122. return false;
  123. }
  124. return true;
  125. }
  126. /**
  127. * Checks if object name is valid
  128. * object naming rules:
  129. * 1. Must be within a length from 1 to 1023 bytes
  130. * 2. Cannot start with '/' or '\\'.
  131. * 3. Must be encoded in UTF-8.
  132. *
  133. * @param string $object Object名称
  134. * @return boolean
  135. */
  136. public static function validateObject($object)
  137. {
  138. $pattern = '/^.{1,1023}$/';
  139. if (!preg_match($pattern, $object) ||
  140. self::startsWith($object, '/') || self::startsWith($object, '\\')
  141. ) {
  142. return false;
  143. }
  144. return true;
  145. }
  146. /**
  147. * Checks if $str starts with $findMe
  148. *
  149. * @param string $str
  150. * @param string $findMe
  151. * @return bool
  152. */
  153. public static function startsWith($str, $findMe)
  154. {
  155. if (strpos($str, $findMe) === 0) {
  156. return true;
  157. } else {
  158. return false;
  159. }
  160. }
  161. /**
  162. * Generate the xml message of createBucketXmlBody.
  163. *
  164. * @param string $storageClass
  165. * @return string
  166. */
  167. public static function createBucketXmlBody($storageClass)
  168. {
  169. $xml = new \SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?><CreateBucketConfiguration></CreateBucketConfiguration>');
  170. $xml->addChild('StorageClass', $storageClass);
  171. return $xml->asXML();
  172. }
  173. /**
  174. * validate $options
  175. *
  176. * @param array $options
  177. * @throws OssException
  178. * @return boolean
  179. */
  180. public static function validateOptions($options)
  181. {
  182. //$options
  183. if ($options != NULL && !is_array($options)) {
  184. throw new OssException ($options . ':' . 'option must be array');
  185. }
  186. }
  187. /**
  188. * check whether the Content is valid.
  189. *
  190. * @param $content string
  191. * @throws OssException
  192. */
  193. public static function validateContent($content)
  194. {
  195. if (empty($content)) {
  196. throw new OssException("http body content is invalid");
  197. }
  198. }
  199. /**
  200. * Check if BUCKET/OBJECT/OBJECT GROUP is empty.
  201. *
  202. * @param string $name
  203. * @param string $errMsg
  204. * @throws OssException
  205. * @return void
  206. */
  207. public static function throwOssExceptionWithMessageIfEmpty($name, $errMsg)
  208. {
  209. if (empty($name)) {
  210. if (is_string($name) && $name == '0')
  211. return;
  212. throw new OssException($errMsg);
  213. }
  214. }
  215. /**
  216. * This is a method for test only. DO NOT USE.
  217. *
  218. * @param $filename
  219. * @param $size
  220. */
  221. public static function generateFile($filename, $size)
  222. {
  223. if (file_exists($filename) && $size == filesize($filename)) {
  224. echo $filename . " already exists, no need to create again. ";
  225. return;
  226. }
  227. $part_size = 1 * 1024 * 1024;
  228. $fp = fopen($filename, "w");
  229. $characters = <<<BBB
  230. 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
  231. BBB;
  232. $charactersLength = strlen($characters);
  233. if ($fp) {
  234. while ($size > 0) {
  235. if ($size < $part_size) {
  236. $write_size = $size;
  237. } else {
  238. $write_size = $part_size;
  239. }
  240. $size -= $write_size;
  241. $a = $characters[rand(0, $charactersLength - 1)];
  242. $content = str_repeat($a, $write_size);
  243. $flag = fwrite($fp, $content);
  244. if (!$flag) {
  245. echo "write to " . $filename . " failed. <br>";
  246. break;
  247. }
  248. }
  249. } else {
  250. echo "open " . $filename . " failed. <br>";
  251. }
  252. fclose($fp);
  253. }
  254. /**
  255. * Get MD5 of the file.
  256. *
  257. * @param $filename
  258. * @param $from_pos
  259. * @param $to_pos
  260. * @return string
  261. */
  262. public static function getMd5SumForFile($filename, $from_pos, $to_pos)
  263. {
  264. $content_md5 = "";
  265. if (($to_pos - $from_pos) > self::OSS_MAX_PART_SIZE) {
  266. return $content_md5;
  267. }
  268. $filesize = filesize($filename);
  269. if ($from_pos >= $filesize || $to_pos >= $filesize || $from_pos < 0 || $to_pos < 0) {
  270. return $content_md5;
  271. }
  272. $total_length = $to_pos - $from_pos + 1;
  273. $buffer = 8192;
  274. $left_length = $total_length;
  275. if (!file_exists($filename)) {
  276. return $content_md5;
  277. }
  278. if (false === $fh = fopen($filename, 'rb')) {
  279. return $content_md5;
  280. }
  281. fseek($fh, $from_pos);
  282. $data = '';
  283. while (!feof($fh)) {
  284. if ($left_length >= $buffer) {
  285. $read_length = $buffer;
  286. } else {
  287. $read_length = $left_length;
  288. }
  289. if ($read_length <= 0) {
  290. break;
  291. } else {
  292. $data .= fread($fh, $read_length);
  293. $left_length = $left_length - $read_length;
  294. }
  295. }
  296. fclose($fh);
  297. $content_md5 = base64_encode(md5($data, true));
  298. return $content_md5;
  299. }
  300. /**
  301. * Check if the OS is Windows. The default encoding in Windows is GBK.
  302. *
  303. * @return bool
  304. */
  305. public static function isWin()
  306. {
  307. return strtoupper(substr(PHP_OS, 0, 3)) == "WIN";
  308. }
  309. /**
  310. * Encodes the file path from GBK to UTF-8.
  311. * The default encoding in Windows is GBK.
  312. * And if the file path is in Chinese, the file would not be found without the transcoding to UTF-8.
  313. *
  314. * @param $file_path
  315. * @return string
  316. */
  317. public static function encodePath($file_path)
  318. {
  319. if (self::chkChinese($file_path) && self::isWin()) {
  320. $file_path = iconv('utf-8', 'gbk', $file_path);
  321. }
  322. return $file_path;
  323. }
  324. /**
  325. * Check if the endpoint is in the IPv4 format, such as xxx.xxx.xxx.xxx:port or xxx.xxx.xxx.xxx.
  326. *
  327. * @param string $endpoint The endpoint to check.
  328. * @return boolean
  329. */
  330. public static function isIPFormat($endpoint)
  331. {
  332. $ip_array = explode(":", $endpoint);
  333. $hostname = $ip_array[0];
  334. $ret = filter_var($hostname, FILTER_VALIDATE_IP);
  335. if (!$ret) {
  336. return false;
  337. } else {
  338. return true;
  339. }
  340. }
  341. /**
  342. * Get the host:port from endpoint.
  343. *
  344. * @param string $endpoint the endpoint.
  345. * @return boolean
  346. */
  347. public static function getHostPortFromEndpoint($endpoint)
  348. {
  349. $str = $endpoint;
  350. $pos = strpos($str, "://");
  351. if ($pos !== false) {
  352. $str = substr($str, $pos+3);
  353. }
  354. $pos = strpos($str, '#');
  355. if ($pos !== false) {
  356. $str = substr($str, 0, $pos);
  357. }
  358. $pos = strpos($str, '?');
  359. if ($pos !== false) {
  360. $str = substr($str, 0, $pos);
  361. }
  362. $pos = strpos($str, '/');
  363. if ($pos !== false) {
  364. $str = substr($str, 0, $pos);
  365. }
  366. $pos = strpos($str, '@');
  367. if ($pos !== false) {
  368. $str = substr($str, $pos+1);
  369. }
  370. if (!preg_match('/^[\w.-]+(:[0-9]+)?$/', $str)) {
  371. throw new OssException("endpoint is invalid:" . $endpoint);
  372. }
  373. return $str;
  374. }
  375. /**
  376. * Generate the xml message of DeleteMultiObjects.
  377. *
  378. * @param string[] $objects
  379. * @param bool $quiet
  380. * @return string
  381. */
  382. public static function createDeleteObjectsXmlBody($objects, $quiet)
  383. {
  384. $xml = new \SimpleXMLElement('<?xml version="1.0" encoding="utf-8"?><Delete></Delete>');
  385. $xml->addChild('Quiet', $quiet);
  386. foreach ($objects as $object) {
  387. $sub_object = $xml->addChild('Object');
  388. $object = OssUtil::sReplace($object);
  389. $sub_object->addChild('Key', $object);
  390. }
  391. return $xml->asXML();
  392. }
  393. /**
  394. * Generate the xml message of DeleteMultiObjects.
  395. *
  396. * @param DeleteObjectInfo[] $objects
  397. * @param bool $quiet
  398. * @return string
  399. */
  400. public static function createDeleteObjectVersionsXmlBody($objects, $quiet)
  401. {
  402. $xml = new \SimpleXMLElement('<?xml version="1.0" encoding="utf-8"?><Delete></Delete>');
  403. $xml->addChild('Quiet', $quiet);
  404. foreach ($objects as $object) {
  405. $sub_object = $xml->addChild('Object');
  406. $key = OssUtil::sReplace($object->getKey());
  407. $sub_object->addChild('Key', $key);
  408. if (!empty($object->getVersionId())) {
  409. $sub_object->addChild('VersionId', $object->getVersionId());
  410. }
  411. }
  412. return $xml->asXML();
  413. }
  414. /**
  415. * Generate the xml message of CompleteMultipartUpload.
  416. *
  417. * @param array[] $listParts
  418. * @return string
  419. */
  420. public static function createCompleteMultipartUploadXmlBody($listParts)
  421. {
  422. $xml = new \SimpleXMLElement('<?xml version="1.0" encoding="utf-8"?><CompleteMultipartUpload></CompleteMultipartUpload>');
  423. foreach ($listParts as $node) {
  424. $part = $xml->addChild('Part');
  425. $part->addChild('PartNumber', $node['PartNumber']);
  426. $part->addChild('ETag', $node['ETag']);
  427. }
  428. return $xml->asXML();
  429. }
  430. /**
  431. * Read the directory, return a associative array in which the MD5 is the named key and the <path,filanme> is the value.
  432. *
  433. * @param string $dir
  434. * @param string $exclude
  435. * @param bool $recursive
  436. * @return string[]
  437. */
  438. public static function readDir($dir, $exclude = ".|..|.svn|.git", $recursive = false)
  439. {
  440. $file_list_array = array();
  441. $base_path = $dir;
  442. $exclude_array = explode("|", $exclude);
  443. $exclude_array = array_unique(array_merge($exclude_array, array('.', '..')));
  444. if ($recursive) {
  445. foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($dir)) as $new_file) {
  446. if ($new_file->isDir()) continue;
  447. $object = str_replace($base_path, '', $new_file);
  448. if (!in_array(strtolower($object), $exclude_array)) {
  449. $object = ltrim($object, '/');
  450. if (is_file($new_file)) {
  451. $key = md5($new_file . $object, false);
  452. $file_list_array[$key] = array('path' => $new_file, 'file' => $object,);
  453. }
  454. }
  455. }
  456. } else if ($handle = opendir($dir)) {
  457. while (false !== ($file = readdir($handle))) {
  458. if (!in_array(strtolower($file), $exclude_array)) {
  459. $new_file = $dir . '/' . $file;
  460. $object = $file;
  461. $object = ltrim($object, '/');
  462. if (is_file($new_file)) {
  463. $key = md5($new_file . $object, false);
  464. $file_list_array[$key] = array('path' => $new_file, 'file' => $object,);
  465. }
  466. }
  467. }
  468. closedir($handle);
  469. }
  470. return $file_list_array;
  471. }
  472. /**
  473. * Decode key based on the encoding type
  474. *
  475. * @param string $key
  476. * @param string $encoding
  477. * @return string
  478. */
  479. public static function decodeKey($key, $encoding)
  480. {
  481. if ($encoding == "") {
  482. return $key;
  483. }
  484. if ($encoding == "url") {
  485. return rawurldecode($key);
  486. } else {
  487. throw new OssException("Unrecognized encoding type: " . $encoding);
  488. }
  489. }
  490. }