OssUtil.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  1. <?php
  2. namespace OSS\Core;
  3. /**
  4. * Class OssUtil
  5. *
  6. * Oss工具类,主要供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. * 生成query params
  21. *
  22. * @param array $options 关联数组
  23. * @return string 返回诸如 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. * 转义字符替换
  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. * 检查是否是中文编码
  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. * 检测是否GB2312编码
  60. *
  61. * @param string $str
  62. * @return boolean false UTF-8编码 TRUE GB2312编码
  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. * 检测是否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. * 检验bucket名称是否合法
  110. * bucket的命名规范:
  111. * 1. 只能包括小写字母,数字
  112. * 2. 必须以小写字母或者数字开头
  113. * 3. 长度必须在3-63字节之间
  114. *
  115. * @param string $bucket Bucket名称
  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. * 检验object名称是否合法
  128. * object命名规范:
  129. * 1. 规则长度必须在1-1023字节之间
  130. * 2. 使用UTF-8编码
  131. * 3. 不能以 "/" "\\"开头
  132. *
  133. * @param string $object Object名称
  134. * @return boolean
  135. */
  136. public static function validateObject($object)
  137. {
  138. $pattern = '/^.{1,1023}$/';
  139. if (empty($object) || !preg_match($pattern, $object) ||
  140. self::startsWith($object, '/') || self::startsWith($object, '\\')
  141. ) {
  142. return false;
  143. }
  144. return true;
  145. }
  146. /**
  147. * 判断字符串$str是不是以$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. * 生成createBucketXmlBody接口的xml消息
  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. * 检验$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. * 检查上传文件的内容是否合法
  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. * 校验BUCKET/OBJECT/OBJECT GROUP是否为空
  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. throw new OssException($errMsg);
  211. }
  212. }
  213. /**
  214. * 仅供测试使用的接口,请勿使用
  215. *
  216. * @param $filename
  217. * @param $size
  218. */
  219. public static function generateFile($filename, $size)
  220. {
  221. if (file_exists($filename) && $size == filesize($filename)) {
  222. echo $filename . " already exists, no need to create again. ";
  223. return;
  224. }
  225. $part_size = 1 * 1024 * 1024;
  226. $fp = fopen($filename, "w");
  227. $characters = <<<BBB
  228. 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
  229. BBB;
  230. $charactersLength = strlen($characters);
  231. if ($fp) {
  232. while ($size > 0) {
  233. if ($size < $part_size) {
  234. $write_size = $size;
  235. } else {
  236. $write_size = $part_size;
  237. }
  238. $size -= $write_size;
  239. $a = $characters[rand(0, $charactersLength - 1)];
  240. $content = str_repeat($a, $write_size);
  241. $flag = fwrite($fp, $content);
  242. if (!$flag) {
  243. echo "write to " . $filename . " failed. <br>";
  244. break;
  245. }
  246. }
  247. } else {
  248. echo "open " . $filename . " failed. <br>";
  249. }
  250. fclose($fp);
  251. }
  252. /**
  253. * 得到文件的md5编码
  254. *
  255. * @param $filename
  256. * @param $from_pos
  257. * @param $to_pos
  258. * @return string
  259. */
  260. public static function getMd5SumForFile($filename, $from_pos, $to_pos)
  261. {
  262. $content_md5 = "";
  263. if (($to_pos - $from_pos) > self::OSS_MAX_PART_SIZE) {
  264. return $content_md5;
  265. }
  266. $filesize = filesize($filename);
  267. if ($from_pos >= $filesize || $to_pos >= $filesize || $from_pos < 0 || $to_pos < 0) {
  268. return $content_md5;
  269. }
  270. $total_length = $to_pos - $from_pos + 1;
  271. $buffer = 8192;
  272. $left_length = $total_length;
  273. if (!file_exists($filename)) {
  274. return $content_md5;
  275. }
  276. if (false === $fh = fopen($filename, 'rb')) {
  277. return $content_md5;
  278. }
  279. fseek($fh, $from_pos);
  280. $data = '';
  281. while (!feof($fh)) {
  282. if ($left_length >= $buffer) {
  283. $read_length = $buffer;
  284. } else {
  285. $read_length = $left_length;
  286. }
  287. if ($read_length <= 0) {
  288. break;
  289. } else {
  290. $data .= fread($fh, $read_length);
  291. $left_length = $left_length - $read_length;
  292. }
  293. }
  294. fclose($fh);
  295. $content_md5 = base64_encode(md5($data, true));
  296. return $content_md5;
  297. }
  298. /**
  299. * 检测是否windows系统,因为windows系统默认编码为GBK
  300. *
  301. * @return bool
  302. */
  303. public static function isWin()
  304. {
  305. return strtoupper(substr(PHP_OS, 0, 3)) == "WIN";
  306. }
  307. /**
  308. * 主要是由于windows系统编码是gbk,遇到中文时候,如果不进行转换处理会出现找不到文件的问题
  309. *
  310. * @param $file_path
  311. * @return string
  312. */
  313. public static function encodePath($file_path)
  314. {
  315. if (self::chkChinese($file_path) && self::isWin()) {
  316. $file_path = iconv('utf-8', 'gbk', $file_path);
  317. }
  318. return $file_path;
  319. }
  320. /**
  321. * 判断用户输入的endpoint是否是 xxx.xxx.xxx.xxx:port 或者 xxx.xxx.xxx.xxx的ip格式
  322. *
  323. * @param string $endpoint 需要做判断的endpoint
  324. * @return boolean
  325. */
  326. public static function isIPFormat($endpoint)
  327. {
  328. $ip_array = explode(":", $endpoint);
  329. $hostname = $ip_array[0];
  330. $ret = filter_var($hostname, FILTER_VALIDATE_IP);
  331. if (!$ret) {
  332. return false;
  333. } else {
  334. return true;
  335. }
  336. }
  337. /**
  338. * 生成DeleteMultiObjects接口的xml消息
  339. *
  340. * @param string[] $objects
  341. * @param bool $quiet
  342. * @return string
  343. */
  344. public static function createDeleteObjectsXmlBody($objects, $quiet)
  345. {
  346. $xml = new \SimpleXMLElement('<?xml version="1.0" encoding="utf-8"?><Delete></Delete>');
  347. $xml->addChild('Quiet', $quiet);
  348. foreach ($objects as $object) {
  349. $sub_object = $xml->addChild('Object');
  350. $object = OssUtil::sReplace($object);
  351. $sub_object->addChild('Key', $object);
  352. }
  353. return $xml->asXML();
  354. }
  355. /**
  356. * 生成CompleteMultipartUpload接口的xml消息
  357. *
  358. * @param array[] $listParts
  359. * @return string
  360. */
  361. public static function createCompleteMultipartUploadXmlBody($listParts)
  362. {
  363. $xml = new \SimpleXMLElement('<?xml version="1.0" encoding="utf-8"?><CompleteMultipartUpload></CompleteMultipartUpload>');
  364. foreach ($listParts as $node) {
  365. $part = $xml->addChild('Part');
  366. $part->addChild('PartNumber', $node['PartNumber']);
  367. $part->addChild('ETag', $node['ETag']);
  368. }
  369. return $xml->asXML();
  370. }
  371. /**
  372. * 读取目录
  373. *
  374. * @param string $dir
  375. * @param string $exclude
  376. * @param bool $recursive
  377. * @return string[]
  378. */
  379. public static function readDir($dir, $exclude = ".|..|.svn|.git", $recursive = false)
  380. {
  381. $file_list_array = array();
  382. $base_path = $dir;
  383. $exclude_array = explode("|", $exclude);
  384. $exclude_array = array_unique(array_merge($exclude_array, array('.', '..')));
  385. if ($recursive) {
  386. foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($dir)) as $new_file) {
  387. if ($new_file->isDir()) continue;
  388. $object = str_replace($base_path, '', $new_file);
  389. if (!in_array(strtolower($object), $exclude_array)) {
  390. $object = ltrim($object, '/');
  391. if (is_file($new_file)) {
  392. $key = md5($new_file . $object, false);
  393. $file_list_array[$key] = array('path' => $new_file, 'file' => $object,);
  394. }
  395. }
  396. }
  397. } else if ($handle = opendir($dir)) {
  398. while (false !== ($file = readdir($handle))) {
  399. if (!in_array(strtolower($file), $exclude_array)) {
  400. $new_file = $dir . '/' . $file;
  401. $object = $file;
  402. $object = ltrim($object, '/');
  403. if (is_file($new_file)) {
  404. $key = md5($new_file . $object, false);
  405. $file_list_array[$key] = array('path' => $new_file, 'file' => $object,);
  406. }
  407. }
  408. }
  409. closedir($handle);
  410. }
  411. return $file_list_array;
  412. }
  413. /**
  414. * Decode key based on the encoding type
  415. *
  416. * @param string $key
  417. * @param string $encoding
  418. * @return string
  419. */
  420. public static function decodeKey($key, $encoding)
  421. {
  422. if ($encoding == "") {
  423. return $key;
  424. }
  425. if ($encoding == "url") {
  426. return rawurldecode($key);
  427. } else {
  428. throw new OssException("Unrecognized encoding type: " . $encoding);
  429. }
  430. }
  431. }