123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718 |
- <?php
- /**
- * +----------------------------------------------------------------------
- * | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
- * +----------------------------------------------------------------------
- * | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
- * +----------------------------------------------------------------------
- * | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
- * +----------------------------------------------------------------------
- * | Author: CRMEB Team <admin@crmeb.com>
- * +----------------------------------------------------------------------
- */
- namespace crmeb\services\upload\extend\obs;
- use crmeb\exceptions\UploadException;
- use crmeb\services\upload\BaseClient;
- use crmeb\services\upload\extend\cos\XML;
- /**
- * 华为云上传
- * Class Client
- * @author 等风来
- * @email 136327134@qq.com
- * @date 2023/5/18
- * @package crmeb\services\upload\extend\obs
- */
- class Client extends BaseClient
- {
- const HEADER_PREFIX = 'x-obs-';
- const INTEREST_HEADER_KEY_LIST = ['content-type', 'content-md5', 'date'];
- const ALTERNATIVE_DATE_HEADER = 'x-obs-date';
- const ALLOWED_RESOURCE_PARAMTER_NAMES = [
- 'acl',
- 'policy',
- 'torrent',
- 'logging',
- 'location',
- 'storageinfo',
- 'quota',
- 'storagepolicy',
- 'requestpayment',
- 'versions',
- 'versioning',
- 'versionid',
- 'uploads',
- 'uploadid',
- 'partnumber',
- 'website',
- 'notification',
- 'lifecycle',
- 'deletebucket',
- 'delete',
- 'cors',
- 'restore',
- 'tagging',
- 'response-content-type',
- 'response-content-language',
- 'response-expires',
- 'response-cache-control',
- 'response-content-disposition',
- 'response-content-encoding',
- 'x-image-process',
- 'backtosource',
- 'storageclass',
- 'replication',
- 'append',
- 'position',
- 'x-oss-process'
- ];
- //桶acl
- const OBS_ACL = [
- [
- 'value' => 'public-read',
- 'label' => '公共读(推荐)',
- ],
- [
- 'value' => 'public-read-write',
- 'label' => '公共读写',
- ],
- ];
- //默认acl
- const DEFAULT_OBS_ACL = 'public-read';
- protected $isCname = false;
- protected $pathStyle;
- /**
- * @var
- */
- protected $accessKeyId;
- /**
- * @var
- */
- protected $secretKey;
- /**
- * 桶名
- * @var string
- */
- protected $bucketName;
- /**
- * 地区
- * @var string
- */
- protected $region;
- /**
- * @var mixed|string
- */
- protected $uploadUrl;
- /**
- * @var string
- */
- protected $baseUrl = 'obs.cn-north-1.myhuaweicloud.com';
- protected $type = 'hw';
- /**
- * Client constructor.
- * @param array $config
- */
- public function __construct(array $config = [])
- {
- $this->accessKeyId = $config['accessKey'] ?? '';
- $this->secretKey = $config['secretKey'] ?? '';
- $this->bucketName = $config['bucket'] ?? '';
- $this->region = $config['region'] ?? 'ap-chengdu';
- $this->uploadUrl = $config['uploadUrl'] ?? '';
- $this->type = $config['type'] ?? 'hw';
- }
- /**
- * 检测
- * @author 等风来
- * @email 136327134@qq.com
- * @date 2023/5/18
- */
- protected function checkOptions()
- {
- if (!$this->bucketName) {
- throw new UploadException('请传入桶名');
- }
- if (!$this->region) {
- throw new UploadException('请传入所属地域');
- }
- if (!$this->accessKeyId) {
- throw new UploadException('请传入SecretId');
- }
- if (!$this->secretKey) {
- throw new UploadException('请传入SecretKey');
- }
- return $this;
- }
- /**
- * 上传图片
- * @param string $key
- * @param $body
- * @return mixed
- * @author 等风来
- * @email 136327134@qq.com
- * @date 2023/5/18
- */
- public function putObject(string $key, $body, string $contentType = 'image/jpeg')
- {
- $header = [
- 'Host' => $this->getRequestUrl($this->bucketName, $this->region),
- 'Content-Type' => $contentType,
- 'Content-Length' => strlen($body),
- ];
- $res = $this->checkOptions()->request('https://' . $header['Host'] . '/' . $key, 'PUT', [
- 'bucket' => $this->bucketName,
- 'body' => $body
- ], $header);
- return $this->response($res);
- }
- /**
- * 删除上传对象
- * @param string $key
- * @return mixed
- * @author 等风来
- * @email 136327134@qq.com
- * @date 2023/5/18
- */
- public function deleteObject(string $key)
- {
- $header = [
- 'Host' => $this->getRequestUrl($this->bucketName, $this->region),
- ];
- $res = $this->request('https://' . $header['Host'] . '/' . $key, 'DELETE', [
- 'bucket' => $this->bucketName
- ], $header);
- return $this->response($res);
- }
- /**
- * 获取桶
- * @return false|string
- * @author 等风来
- * @email 136327134@qq.com
- * @date 2023/5/16
- */
- public function listBuckets()
- {
- $header = [
- 'Host' => $this->getRequestUrl('', $this->region),
- ];
- $res = $this->request('https://' . $header['Host'] . '/', 'GET', [], []);
- return $this->response($res);
- }
- public function headBucket(string $bucket, string $region)
- {
- $header = [
- 'Host' => $this->getRequestUrl($bucket, $region),
- ];
- $res = $this->request('https://' . $header['Host'] . '/', 'HEAD', [], []);
- return $this->response($res);
- }
- /**
- * 设置桶的策略
- * @param string $bucket
- * @param string $region
- * @param array $data
- * @return mixed
- *
- * @date 2023/06/08
- * @author yyw
- */
- public function putPolicy(string $bucket, string $region, array $data)
- {
- $header = [
- 'Host' => $this->getRequestUrl($bucket, $region),
- "Content-Type" => "application/json"
- ];
- $res = $this->request('https://' . $header['Host'] . '/?policy', 'PUT', [
- 'bucket' => $bucket,
- 'json' => $data
- ], $header);
- return $this->response($res);
- }
- /**
- * 创建桶
- * @param string $bucket
- * @param string $region
- * @param string $acl
- * @return mixed
- * @author 等风来
- * @email 136327134@qq.com
- * @date 2023/5/18
- */
- public function createBucket(string $bucket, string $region, string $acl = self::DEFAULT_OBS_ACL)
- {
- $header = [
- 'x-obs-acl' => $acl,
- 'Host' => $this->getRequestUrl($bucket, $region),
- "Content-Type" => "application/xml"
- ];
- $xml = "<CreateBucketConfiguration><Location>{$region}</Location></CreateBucketConfiguration>";
- $res = $this->request('https://' . $header['Host'] . '/', 'PUT', [
- 'bucket' => $bucket,
- 'body' => $xml
- ], $header);
- return $this->response($res);
- }
- /**
- * 删除桶
- * @param string $bucket
- * @param string $region
- * @return mixed
- * @author 等风来
- * @email 136327134@qq.com
- * @date 2023/5/18
- */
- public function deleteBucket(string $bucket, string $region)
- {
- $header = [
- 'Host' => $this->getRequestUrl($bucket, $region),
- ];
- $res = $this->request('https://' . $header['Host'] . '/', 'DELETE', [
- 'bucket' => $bucket
- ], $header);
- return $this->response($res);
- }
- /**
- * 获取桶的自定义域名
- * @param string $bucket
- * @param string $region
- * @author 等风来
- * @email 136327134@qq.com
- * @date 2023/5/18
- */
- public function getBucketDomain(string $bucket, string $region)
- {
- $header = [
- 'Host' => $this->getRequestUrl($bucket, $region),
- ];
- $res = $this->request('https://' . $header['Host'] . '/?customdomain', 'GET', [
- 'bucket' => $bucket
- ], $header);
- return $this->response($res);
- }
- /**
- * 设置桶的自定义域名
- * @param string $bucket
- * @param string $region
- * @param array $data
- * @return mixed
- * @author 等风来
- * @email 136327134@qq.com
- * @date 2023/5/18
- */
- public function putBucketDomain(string $bucket, string $region, array $data = [])
- {
- $header = [
- 'Host' => $this->getRequestUrl($bucket, $region),
- ];
- $res = $this->request('https://' . $header['Host'] . '/?customdomain=' . $data['domainname'], 'PUT', [
- 'bucket' => $bucket
- ], $header);
- return $this->response($res);
- }
- /**
- * 设置跨域
- * @return bool
- * @author 等风来
- * @email 136327134@qq.com
- * @date 2023/5/18
- */
- public function putBucketCors(string $bucket, string $region, array $data = [])
- {
- $xml = $this->xmlBuild($data, 'CORSConfiguration', 'CORSRule');
- $header = [
- 'Host' => $this->getRequestUrl($bucket, $region),
- 'Content-Type' => 'application/xml',
- 'Content-Length' => strlen($xml),
- 'Content-MD5' => base64_encode(md5($xml, true))
- ];
- $res = $this->request('https://' . $header['Host'] . '/?cors', 'PUT', [
- 'bucket' => $bucket,
- 'body' => $xml
- ], $header);
- return $this->response($res);
- }
- /**
- * 删除跨域
- * @param string $bucket
- * @param string $region
- * @return mixed
- *
- * @date 2023/06/08
- * @author yyw
- */
- public function deleteBucketCors(string $bucket, string $region)
- {
- $header = [
- 'Host' => $this->getRequestUrl($bucket, $region),
- ];
- $res = $this->request('https://' . $header['Host'] . '/?cors', 'DELETE', [
- 'bucket' => $bucket,
- ], $header);
- return $this->response($res);
- }
- /**
- * @param $res
- * @return mixed
- * @author 等风来
- * @email 136327134@qq.com
- * @date 2023/5/18
- */
- protected function response($res)
- {
- if (!empty($res['Code']) && !empty($res['Message'])) {
- throw new UploadException($res['Message']);
- }
- return $res;
- }
- /**
- * 获取请求域名
- * @param string $bucket
- * @param string $region
- * @return string
- *
- * @date 2023/06/08
- * @author yyw
- */
- protected function getRequestUrl(string $bucket = '', string $region = '')
- {
- if ($this->type == 'hw') {
- $url = '.myhuaweicloud.com'; // 华为
- } else {
- $url = '.ctyun.cn'; // 天翼
- }
- if ($bucket) {
- return $bucket . '.obs.' . $region . $url;
- } else {
- return 'obs.' . $region . $url;
- }
- }
- /**
- * 地域名称
- * @return \string[][]
- * @author 等风来
- * @email 136327134@qq.com
- * @date 2023/5/17
- */
- public function getRegion()
- {
- return [
- [
- 'value' => 'cn-north-1',
- 'label' => '华北-北京一',
- ],
- // [
- // 'value' => 'cn-north-4',
- // 'label' => '华北-北京四',
- // ],
- [
- 'value' => 'cn-north-9',
- 'label' => '华北-乌兰察布一',
- ],
- [
- 'value' => 'cn-east-2',
- 'label' => '华东-上海二',
- ],
- [
- 'value' => 'cn-east-3',
- 'label' => '华东-上海一',
- ],
- [
- 'value' => 'cn-south-1',
- 'label' => '华南-广州',
- ],
- [
- 'value' => 'ap-southeast-1',
- 'label' => '中国-香港',
- ],
- [
- 'value' => 'cn-south-4',
- 'label' => '华南-广州-友好用户环境',
- ],
- [
- 'value' => 'cn-southwest-2',
- 'label' => '西南-贵阳一',
- ],
- [
- 'value' => 'la-north-2',
- 'label' => '拉美-墨西哥城二',
- ],
- [
- 'value' => 'na-mexico-1',
- 'label' => '拉美-墨西哥城一',
- ],
- [
- 'value' => 'sa-brazil-1',
- 'label' => '拉美-圣保罗一',
- ],
- [
- 'value' => 'la-south-2',
- 'label' => '拉美-圣地亚哥',
- ],
- [
- 'value' => 'tr-west-1',
- 'label' => '土耳其-伊斯坦布尔',
- ],
- [
- 'value' => 'ap-southeast-2',
- 'label' => '亚太-曼谷',
- ],
- [
- 'value' => 'ap-southeast-3',
- 'label' => '亚太-新加坡',
- ],
- [
- 'value' => 'af-south-1',
- 'label' => '非洲-约翰内斯堡',
- ]
- ];
- }
- /**
- * 设置桶名
- * @param string $bucketName
- * @return $this
- * @author 等风来
- * @email 136327134@qq.com
- * @date 2023/5/16
- */
- public function setBucketName(string $bucketName)
- {
- $this->bucketName = $bucketName;
- return $this;
- }
- /**
- * 获取签名
- * @param array $result
- * @return array
- * @author 等风来
- * @email 136327134@qq.com
- * @date 2023/5/17
- */
- protected function getSign(array $result)
- {
- $result['headers']['Date'] = gmdate('D, d M Y H:i:s \G\M\T');
- $canonicalstring = $this->makeCanonicalstring($result['method'], $result['headers'], $result['pathArgs'], $result['dnsParam'], $result['uriParam']);
- $result['cannonicalRequest'] = $canonicalstring;
- $signature = base64_encode(hash_hmac('sha1', $canonicalstring, $this->secretKey, true));
- $authorization = 'OBS ' . $this->accessKeyId . ':' . $signature;
- $result['headers']['Authorization'] = $authorization;
- return $result;
- }
- /**
- * 处理签名数据
- * @param $method
- * @param $headers
- * @param $pathArgs
- * @param $bucketName
- * @param $objectKey
- * @param null $expires
- * @return string
- * @author 等风来
- * @email 136327134@qq.com
- * @date 2023/5/17
- */
- public function makeCanonicalstring($method, $headers, $pathArgs, $bucketName, $objectKey, $expires = null)
- {
- $buffer = [];
- $buffer[] = $method;
- $buffer[] = "\n";
- $interestHeaders = [];
- foreach ($headers as $key => $value) {
- $key = strtolower($key);
- if (in_array($key, self::INTEREST_HEADER_KEY_LIST) || strpos($key, self::HEADER_PREFIX) === 0) {
- $interestHeaders[$key] = $value;
- }
- }
- if (array_key_exists(self::ALTERNATIVE_DATE_HEADER, $interestHeaders)) {
- $interestHeaders['date'] = '';
- }
- if ($expires !== null) {
- $interestHeaders['date'] = strval($expires);
- }
- if (!array_key_exists('content-type', $interestHeaders)) {
- $interestHeaders['content-type'] = '';
- }
- if (!array_key_exists('content-md5', $interestHeaders)) {
- $interestHeaders['content-md5'] = '';
- }
- ksort($interestHeaders);
- foreach ($interestHeaders as $key => $value) {
- if (strpos($key, self::HEADER_PREFIX) === 0) {
- $buffer[] = $key . ':' . $value;
- } else {
- $buffer[] = $value;
- }
- $buffer[] = "\n";
- }
- $uri = '';
- $bucketName = $this->isCname ? $headers['Host'] : $bucketName;
- if ($bucketName) {
- $uri .= '/';
- $uri .= $bucketName;
- if (!$this->pathStyle) {
- $uri .= '/';
- }
- }
- if ($objectKey) {
- if (!($pos = strripos($uri, '/')) || strlen($uri) - 1 !== $pos) {
- $uri .= '/';
- }
- $uri .= $objectKey;
- }
- $buffer[] = $uri === '' ? '/' : $uri;
- if (!empty($pathArgs)) {
- ksort($pathArgs);
- $_pathArgs = [];
- foreach ($pathArgs as $key => $value) {
- if (in_array(strtolower($key), self::ALLOWED_RESOURCE_PARAMTER_NAMES) || strpos($key, self::HEADER_PREFIX) === 0) {
- $_pathArgs[] = $value === null || $value === '' ? $key : $key . '=' . urldecode($value);
- }
- }
- if (!empty($_pathArgs)) {
- $buffer[] = '?';
- $buffer[] = implode('&', $_pathArgs);
- }
- }
- return implode('', $buffer);
- }
- /**
- * 发起请求
- * @param string $url
- * @param string $method
- * @param array $data
- * @param array $clientHeader
- * @param int $timeout
- * @return false|string
- * @author 等风来
- * @email 136327134@qq.com
- * @date 2023/5/16
- */
- public function request(string $url, string $method, array $data = [], array $clientHeader = [], int $timeout = 10)
- {
- $method = strtoupper($method);
- $urlAttr = pathinfo($url);
- $urlParse = parse_url($urlAttr['dirname'] ?? '');
- $uriParam = '';
- if ($urlAttr['dirname'] !== 'https:') {
- if (isset($urlParse['path'])) {
- $uriParam .= substr($urlParse['path'], 1) . '/';
- }
- if (isset($urlAttr['basename'])) {
- $uriParam .= $urlAttr['basename'];
- }
- }
- $result = $this->getSign([
- 'method' => $method,
- 'headers' => $clientHeader,
- 'pathArgs' => '',
- 'dnsParam' => $data['bucket'] ?? '',
- 'uriParam' => $uriParam,
- ]);
- return $this->requestClient($url, $method, $data, $result['headers'], $timeout);
- }
- /**
- * 组合成xml
- * @param array $data
- * @param string $root
- * @param string $itemKey
- * @return string
- * @author 等风来
- * @email 136327134@qq.com
- * @date 2022/10/17
- */
- protected function xmlBuild(array $xmlAttr, string $root = 'xml', string $itemKey = 'item')
- {
- $xml = '<' . $root . '>';
- $xml .= '<' . $itemKey . '>';
- foreach ($xmlAttr as $kk => $vv) {
- if (is_array($vv)) {
- foreach ($vv as $v) {
- $xml .= '<' . $kk . '>' . $v . '</' . $kk . '>';
- }
- } else {
- $xml .= '<' . $kk . '>' . $vv . '</' . $kk . '>';
- }
- }
- $xml .= '</' . $itemKey . '>';
- $xml .= '</' . $root . '>';
- return $xml;
- }
- }
|