AliyunOss.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  1. <?php
  2. namespace Api;
  3. use think\Config;
  4. use OSS\Model\RefererConfig;
  5. use service\SystemConfigService;
  6. use OSS\OssClient;
  7. use OSS\Core\OssException;
  8. use OSS\Core\OssUtil;
  9. /**
  10. * Class AliyunOss
  11. * @package Api
  12. */
  13. class AliyunOss extends AliyunSdk
  14. {
  15. /**
  16. * OSS存储桶名
  17. * @var string
  18. */
  19. protected $OssBucket;
  20. /**
  21. * OSS 地域节点
  22. * @var string
  23. */
  24. protected $OssEndpoint;
  25. /**
  26. * 外网访问地址
  27. * @var string
  28. */
  29. protected $uploadUrl;
  30. /**
  31. * 全局唯一的uploadId
  32. * @var string
  33. */
  34. protected $uploadId;
  35. /**
  36. * 正在上传的文件名
  37. * @var string
  38. */
  39. protected $object;
  40. /**
  41. * 上传验证规则
  42. * @var string
  43. */
  44. protected $autoValidate;
  45. /**
  46. * 是否开启防盗链
  47. * @var array
  48. */
  49. protected $referer;
  50. /**
  51. * 初始化参数
  52. */
  53. protected function _initialize()
  54. {
  55. $this->OssEndpoint = isset($this->config['OssEndpoint']) ? $this->config['OssEndpoint'] : null;
  56. $this->OssBucket = isset($this->config['OssBucket']) ? $this->config['OssBucket'] : null;
  57. $this->uploadUrl = isset($this->config['uploadUrl']) ? $this->config['uploadUrl'] : null;
  58. $this->checkUploadUrl();
  59. $this->referer = isset($this->config['referer']) && is_array($this->config['referer']) ? $this->config['referer'] : [];
  60. }
  61. /**
  62. * 验证合法上传域名
  63. */
  64. protected function checkUploadUrl()
  65. {
  66. $site_url=SystemConfigService::get('site_url');
  67. if($site_url){
  68. $arr = parse_url($site_url);
  69. if($arr['scheme']){
  70. $scheme=$arr['scheme'];
  71. }else{
  72. $scheme='http';
  73. }
  74. }else{
  75. $scheme='http';
  76. }
  77. if ($this->uploadUrl) {
  78. if ($scheme=='https') {
  79. if (strstr($this->uploadUrl, 'https') === false) {
  80. $this->uploadUrl = 'https://' . $this->uploadUrl;
  81. }
  82. }else{
  83. if (strstr($this->uploadUrl, 'http') === false) {
  84. $this->uploadUrl = 'http://' . $this->uploadUrl;
  85. }
  86. }
  87. }
  88. }
  89. /**
  90. * 初始化
  91. * @return null|\OSS\OssClient
  92. * @throws \OSS\Core\OssException
  93. */
  94. public function init()
  95. {
  96. if ($this->client === null) {
  97. $this->client = new OssClient($this->AccessKey, $this->AccessKeySecret, $this->OssEndpoint);
  98. if (!$this->client->doesBucketExist($this->OssBucket)) {
  99. $this->client->createBucket($this->OssBucket, OssClient::OSS_ACL_TYPE_PUBLIC_READ_WRITE);
  100. }
  101. if ($this->referer) {
  102. $refererConfig = new RefererConfig();
  103. // 设置允许空Referer。
  104. $refererConfig->setAllowEmptyReferer(true);
  105. foreach ($this->referer as $url) {
  106. $refererConfig->addReferer($url);
  107. }
  108. $this->client->putBucketReferer($this->OssBucket, $refererConfig);
  109. }
  110. }
  111. return $this->client;
  112. }
  113. /**
  114. * 设置防盗链
  115. * @param array $referer
  116. * @return $this
  117. */
  118. public function setReferer(array $referer = [])
  119. {
  120. $this->referer = $referer;
  121. return $this;
  122. }
  123. /**
  124. * 验证规则
  125. * @param array $autoValidate
  126. * @return $this
  127. */
  128. public function validate(array $autoValidate = [])
  129. {
  130. if (!$autoValidate) {
  131. $autoValidate = Config::get('upload.Validate');
  132. }
  133. $this->autoValidate = $autoValidate;
  134. return $this;
  135. }
  136. /**
  137. * 设置OSS存储桶名
  138. * @param string $OssBucket
  139. * @return $this
  140. * */
  141. public function setOssBucketAttr($OssBucket)
  142. {
  143. $this->OssBucket = $OssBucket;
  144. return $this;
  145. }
  146. /**
  147. * 设置OSS存储外网访问域名
  148. * @param string $OssEndpoint
  149. * @return $this
  150. * */
  151. public function setOssEndpointAttr($OssEndpoint)
  152. {
  153. $this->OssEndpoint = $OssEndpoint;
  154. return $this;
  155. }
  156. /**
  157. * 提取文件名
  158. * @param string $path
  159. * @param string $ext
  160. * @return string
  161. */
  162. protected function saveFileName($path = null, $ext = 'jpg')
  163. {
  164. return ($path ? substr(md5($path), 0, 5) : '') . date('YmdHis') . rand(0, 9999) . '.' . $ext;
  165. }
  166. /**
  167. * 获取文件后缀
  168. * @param \think\File $file
  169. * @return string
  170. */
  171. protected function getExtension(\think\File $file)
  172. {
  173. $pathinfo = pathinfo($file->getInfo('name'));
  174. return isset($pathinfo['extension']) ? $pathinfo['extension'] : '';
  175. }
  176. /**
  177. * 上传图片
  178. * @param $fileName
  179. * @return bool
  180. */
  181. public function upload($fileName)
  182. {
  183. $fileHandle = request()->file($fileName);
  184. $key = $this->saveFileName($fileHandle->getRealPath(), $this->getExtension($fileHandle));
  185. try {
  186. if ($this->autoValidate) {
  187. $fileHandle->validate($this->autoValidate);
  188. $this->autoValidate = null;
  189. }
  190. $uploadInfo = $this->init()->uploadFile($this->OssBucket, $key, $fileHandle->getRealPath());
  191. if (!isset($uploadInfo['info']['url'])) {
  192. return self::setErrorInfo('Upload failure');
  193. }
  194. return [
  195. 'url' => $uploadInfo['info']['url'],
  196. 'key' => $key
  197. ];
  198. } catch (\Throwable $e) {
  199. return self::setErrorInfo($e);
  200. }
  201. }
  202. /**文件分片上传
  203. * @param $fileName
  204. */
  205. public function sliceFileUpload($fileName)
  206. {
  207. $fileHandle = request()->file($fileName);
  208. $key = $this->saveFileName($fileHandle->getRealPath(), $this->getExtension($fileHandle));
  209. $this->object = $object = $key; //上传时生成的文件名
  210. $uploadFile = $fileHandle->getRealPath(); //文件真实路径
  211. /**
  212. * 步骤1:初始化一个分片上传事件,获取uploadId。
  213. */
  214. try{
  215. $ossClient = $this->init();
  216. //返回uploadId。uploadId是分片上传事件的唯一标识,您可以根据uploadId发起相关的操作,如取消分片上传、查询分片上传等。
  217. $this->uploadId = $uploadId = $ossClient->initiateMultipartUpload($this->OssBucket, $object);
  218. } catch(OssException $e) {
  219. return self::setErrorInfo($e->getMessage());
  220. }
  221. /*
  222. * 步骤2:上传分片。
  223. */
  224. $partSize = 10 * 1024 * 1024;
  225. $uploadFileSize = filesize($uploadFile);
  226. $pieces = $ossClient->generateMultiuploadParts($uploadFileSize, $partSize);
  227. $responseUploadPart = array();
  228. $uploadPosition = 0;
  229. $isCheckMd5 = true;
  230. foreach ($pieces as $i => $piece) {
  231. $fromPos = $uploadPosition + (integer)$piece[$ossClient::OSS_SEEK_TO];
  232. $toPos = (integer)$piece[$ossClient::OSS_LENGTH] + $fromPos - 1;
  233. $upOptions = array(
  234. // 上传文件。
  235. $ossClient::OSS_FILE_UPLOAD => $uploadFile,
  236. // 设置分片号。
  237. $ossClient::OSS_PART_NUM => ($i + 1),
  238. // 指定分片上传起始位置。
  239. $ossClient::OSS_SEEK_TO => $fromPos,
  240. // 指定文件长度。
  241. $ossClient::OSS_LENGTH => $toPos - $fromPos + 1,
  242. // 是否开启MD5校验,true为开启。
  243. $ossClient::OSS_CHECK_MD5 => $isCheckMd5,
  244. );
  245. // 开启MD5校验。
  246. if ($isCheckMd5) {
  247. $contentMd5 = OssUtil::getMd5SumForFile($uploadFile, $fromPos, $toPos);
  248. $upOptions[$ossClient::OSS_CONTENT_MD5] = $contentMd5;
  249. }
  250. try {
  251. // 上传分片。
  252. $responseUploadPart[] = $ossClient->uploadPart($this->OssBucket, $object, $uploadId, $upOptions);
  253. } catch(OssException $e) {
  254. return self::setErrorInfo($e->getMessage());
  255. }
  256. }
  257. // $uploadParts是由每个分片的ETag和分片号(PartNumber)组成的数组。
  258. $uploadParts = array();
  259. foreach ($responseUploadPart as $i => $eTag) {
  260. $uploadParts[] = array(
  261. 'PartNumber' => ($i + 1),
  262. 'ETag' => $eTag,
  263. );
  264. }
  265. /**
  266. * 步骤3:完成上传。
  267. */
  268. try {
  269. // 执行completeMultipartUpload操作时,需要提供所有有效的$uploadParts。OSS收到提交的$uploadParts后,会逐一验证每个分片的有效性。当所有的数据分片验证通过后,OSS将把这些分片组合成一个完整的文件。
  270. $res=$ossClient->completeMultipartUpload($this->OssBucket, $object, $uploadId, $uploadParts);
  271. $this->object='';
  272. $this->uploadId='';
  273. } catch(OssException $e) {
  274. return self::setErrorInfo($e->getMessage());
  275. }
  276. $url=$res['info']['url'];
  277. $data=explode('?',$url);
  278. return [
  279. 'url' => $data[0],
  280. 'key' => $key
  281. ];
  282. }
  283. /**取消分片上传
  284. * @param $object 上传的文件名
  285. * @param $upload_id 上传时生成的上传ID
  286. * @return string
  287. */
  288. public function cancelFragmentUpload()
  289. {
  290. try{
  291. $ossClient = $this->init();
  292. $res=$ossClient->abortMultipartUpload($this->OssBucket, $this->object, $this->uploadId);
  293. } catch(OssException $e) {
  294. return $e->getMessage();
  295. }
  296. dump($res);exit;
  297. }
  298. /**
  299. * 文件流上传
  300. * @param string $fileContent
  301. * @param string|null $key
  302. * @return bool|mixed
  303. */
  304. public function stream(string $fileContent, string $key = null)
  305. {
  306. try {
  307. if (!$key) {
  308. $key = $this->saveFileName();
  309. }
  310. $uploadInfo = $this->init()->putObject($this->OssBucket, $key, $fileContent);
  311. if (!isset($uploadInfo['info']['url'])) {
  312. return self::setErrorInfo('Upload failure');
  313. }
  314. return [
  315. 'url' => $uploadInfo['info']['url'],
  316. 'key' => $key
  317. ];
  318. } catch (Throwable $e) {
  319. return self::setErrorInfo($e);
  320. }
  321. }
  322. /**
  323. * 删除指定资源
  324. * @param 资源key
  325. * @return array
  326. * */
  327. public function delOssFile($key)
  328. {
  329. try {
  330. return $this->init()->deleteObject($this->OssBucket, $key);
  331. } catch (\Exception $e) {
  332. return self::setErrorInfo($e);
  333. }
  334. }
  335. /**
  336. * 获取签名
  337. * @param string $callbackUrl
  338. * @param string $dir
  339. * @return string
  340. */
  341. public function getSignature($callbackUrl = '', $dir = '')
  342. {
  343. $base64CallbackBody = base64_encode(json_encode([
  344. 'callbackUrl' => $callbackUrl,
  345. 'callbackBody' => 'filename=${object}&size=${size}&mimeType=${mimeType}&height=${imageInfo.height}&width=${imageInfo.width}',
  346. 'callbackBodyType' => "application/x-www-form-urlencoded"
  347. ]));
  348. $policy = json_encode([
  349. 'expiration' => $this->gmtIso8601(time() + 30),
  350. 'conditions' =>
  351. [
  352. [0 => 'content-length-range', 1 => 0, 2 => 1048576000],
  353. [0 => 'starts-with', 1 => '$key', 2 => $dir]
  354. ]
  355. ]);
  356. $base64Policy = base64_encode($policy);
  357. $signature = base64_encode(hash_hmac('sha1', $base64Policy, $this->AccessKeySecret, true));
  358. return [
  359. 'accessid' => $this->AccessKey,
  360. 'host' => $this->uploadUrl,
  361. 'policy' => $base64Policy,
  362. 'signature' => $signature,
  363. 'expire' => time() + 30,
  364. 'callback' => $base64CallbackBody
  365. ];
  366. }
  367. /**
  368. * 获取ISO时间格式
  369. * @param $time
  370. * @return string
  371. */
  372. protected function gmtIso8601($time)
  373. {
  374. $dtStr = date("c", $time);
  375. $mydatetime = new \DateTime($dtStr);
  376. $expiration = $mydatetime->format(\DateTime::ISO8601);
  377. $pos = strpos($expiration, '+');
  378. $expiration = substr($expiration, 0, $pos);
  379. return $expiration . "Z";
  380. }
  381. /**
  382. * 获取防盗链信息
  383. * @param string $bucket
  384. * @return RefererConfig
  385. * @throws \OSS\Core\OssException
  386. */
  387. public function getBucketReferer($bucket = '')
  388. {
  389. return $this->init()->getBucketReferer($bucket ? $bucket : $this->OssBucket);
  390. }
  391. /**
  392. * 清除防盗链
  393. * @param string $bucket
  394. * @return \OSS\Http\ResponseCore
  395. * @throws \OSS\Core\OssException
  396. */
  397. public function deleteBucketReferer($bucket = '')
  398. {
  399. $refererConfig = new RefererConfig();
  400. return $this->init()->putBucketReferer($bucket ? $bucket : $this->OssBucket, $refererConfig);
  401. }
  402. }