zx/crmeb/services/upload/storage/Oss.php

575 lines
20 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2023 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace crmeb\services\upload\storage;
use crmeb\services\upload\BaseUpload;
use crmeb\exceptions\AdminException;
use crmeb\exceptions\UploadException;
use GuzzleHttp\Psr7\Stream;
use OSS\Core\OssException;
use OSS\Model\CorsConfig;
use OSS\Model\CorsRule;
use OSS\OssClient;
use think\exception\ValidateException;
use function Qiniu\base64_urlSafeEncode;
/**
* 阿里云OSS上传
* Class OSS
*/
class Oss extends BaseUpload
{
/**
* accessKey
* @var mixed
*/
protected $accessKey;
/**
* secretKey
* @var mixed
*/
protected $secretKey;
/**
* 句柄
* @var OssClient
*/
protected $handle;
/**
* 空间域名 Domain
* @var mixed
*/
protected $uploadUrl;
/**
* 存储空间名称 公开空间
* @var mixed
*/
protected $storageName;
/**
* COS使用 所属地域
* @var mixed|null
*/
protected $storageRegion;
/**
* 水印位置
* @var string[]
*/
protected $position = [
'1' => 'nw',//:左上
'2' => 'north',//:中上
'3' => 'ne',//:右上
'4' => 'west',//:左中
'5' => 'center',//:中部
'6' => 'east',//:右中
'7' => 'sw',//:左下
'8' => 'south',//:中下
'9' => 'se',//:右下
];
/**
* @var string
*/
protected $cdn;
/**
* 初始化
* @param array $config
* @return mixed|void
*/
protected function initialize(array $config)
{
parent::initialize($config);
$this->accessKey = $config['accessKey'] ?? null;
$this->secretKey = $config['secretKey'] ?? null;
$this->uploadUrl = $this->checkUploadUrl($config['uploadUrl'] ?? '');
$this->storageName = $config['storageName'] ?? null;
$this->cdn = $config['cdn'] ?? null;
$this->storageRegion = $config['storageRegion'] ?? null;
}
/**
* 初始化oss
* @return OssClient
* @throws OssException
*/
protected function app()
{
if (!$this->accessKey || !$this->secretKey) {
throw new UploadException(400721);
}
$this->handle = new OssClient($this->accessKey, $this->secretKey, $this->storageRegion);
//不再自动创建
// if (!$this->handle->doesBucketExist($this->storageName)) {
// $this->handle->createBucket($this->storageName, OssClient::OSS_ACL_TYPE_PUBLIC_READ_WRITE);
// }
return $this->handle;
}
/**
* 上传文件
* @param string $file
* @param bool $realName
* @return array|bool|mixed|\StdClass
*/
public function move(string $file = 'file', $realName = false)
{
$fileHandle = app()->request->file($file);
if (!$fileHandle) {
return $this->setError('上传的文件不存在');
}
if ($this->validate) {
try {
validate([$file => $this->validate])->check([$fileHandle]);
} catch (ValidateException $e) {
return $this->setError($e->getMessage());
}
}
$key = $this->saveFileName($fileHandle->getRealPath(), $fileHandle->getOriginalExtension());
$key = $this->getUploadPath($key);
try {
$uploadInfo = $this->app()->uploadFile($this->storageName, $key, $fileHandle->getRealPath());
if (!isset($uploadInfo['info']['url'])) {
return $this->setError('Upload failure');
}
$this->fileInfo->uploadInfo = $uploadInfo;
$this->fileInfo->realName = $fileHandle->getOriginalName();
$this->fileInfo->filePath = ($this->cdn ?: $this->uploadUrl) . '/' . $key;
$this->fileInfo->fileName = $key;
// $this->fileInfo->filePathWater = $this->water($this->fileInfo->filePath);
// $this->authThumb && $this->thumb($this->fileInfo->filePath);
return $this->fileInfo;
} catch (UploadException $e) {
return $this->setError($e->getMessage());
}
}
/**
* 文件流上传
* @param $fileContent
* @param string|null $key
* @return array|bool|mixed
* @throws OssException
*/
public function stream($fileContent, string $key = null)
{
try {
if (!$key) {
$key = $this->saveFileName();
}
$key = $this->getUploadPath($key);
if (is_resource($fileContent)) {
$fileContent = new Stream($fileContent);
}
$uploadInfo = $this->app()->putObject($this->storageName, $key, $fileContent);
if (!isset($uploadInfo['info']['url'])) {
return $this->setError('Upload failure');
}
$this->fileInfo->uploadInfo = $uploadInfo;
$this->fileInfo->realName = $key;
$this->fileInfo->filePath = ($this->cdn ?: $this->uploadUrl) . '/' . $key;
$this->fileInfo->fileName = $key;
$this->fileInfo->filePathWater = $this->water($this->fileInfo->filePath);
$this->thumb($this->fileInfo->filePath);
return $this->fileInfo;
} catch (UploadException $e) {
return $this->setError($e->getMessage());
}
}
/**
* 缩略图
* @param string $filePath
* @param string $fileName
* @param string $type
* @return array|mixed
*/
public function thumb(string $filePath = '', string $fileName = '', string $type = 'all')
{
$filePath = $this->getFilePath($filePath);
$data = ['big' => $filePath, 'mid' => $filePath, 'small' => $filePath];
$this->fileInfo->filePathBig = $this->fileInfo->filePathMid = $this->fileInfo->filePathSmall = $this->fileInfo->filePathWater = $filePath;
if ($filePath) {
$config = $this->thumbConfig;
foreach ($this->thumb as $v) {
if ($type == 'all' || $type == $v) {
$height = 'thumb_' . $v . '_height';
$width = 'thumb_' . $v . '_width';
$key = 'filePath' . ucfirst($v);
// if (systemConfig('image_thumbnail_status') && isset($config[$height]) && isset($config[$width])) {
if (isset($config[$height]) && isset($config[$width])) {
$this->fileInfo->$key = $filePath . '?x-oss-process=image/resize,w_' . $config[$width];
$this->fileInfo->$key = $this->water($this->fileInfo->$key);
$data[$v] = $this->fileInfo->$key;
} else {
$this->fileInfo->$key = $this->water($this->fileInfo->$key);
$data[$v] = $this->fileInfo->$key;
}
}
}
}
return $data;
}
/**
* 水印
* @param string $filePath
* @return mixed|string
*/
public function water(string $filePath = '')
{
$filePath = $this->getFilePath($filePath);
$waterConfig = $this->waterConfig;
$waterPath = $filePath;
if ($waterConfig['image_watermark_status'] && $filePath) {
if (strpos($filePath, '?x-oss-process') === false) {
$filePath .= '?x-oss-process=image';
}
switch ($waterConfig['watermark_type']) {
case 1://图片
if (!$waterConfig['watermark_image']) {
throw new AdminException(400722);
}
$waterPath = $filePath . '/watermark,image_' . base64_urlSafeEncode(substr(parse_url($waterConfig['watermark_image'],PHP_URL_PATH), 1)) . ',t_' . $waterConfig['watermark_opacity'] . ',g_' . ($this->position[$waterConfig['watermark_position']] ?? 'nw') . ',x_' . $waterConfig['watermark_x'] . ',y_' . $waterConfig['watermark_y'];
break;
case 2://文字
if (!$waterConfig['watermark_text']) {
throw new AdminException(400723);
}
$waterConfig['watermark_text_color'] = str_replace('#', '', $waterConfig['watermark_text_color']);
$waterPath = $filePath . '/watermark,text_' . base64_encode($waterConfig['watermark_text']) . ',color_' . $waterConfig['watermark_text_color'] . ',size_' . $waterConfig['watermark_text_size'] . ',g_' . ($this->position[$waterConfig['watermark_position']] ?? 'nw') . ',x_' . $waterConfig['watermark_x'] . ',y_' . $waterConfig['watermark_y'];
break;
}
}
return $waterPath;
}
/**
* 删除资源
* @param $key
* @return mixed
*/
public function delete(string $key)
{
try {
return $this->app()->deleteObject($this->storageName, $key);
} catch (OssException $e) {
return $this->setError($e->getMessage());
}
}
/**
* 获取OSS上传密钥
* @return mixed|void
*/
public function getTempKeys($callbackUrl = '', $dir = '')
{
// TODO: Implement getTempKeys() method.
$base64CallbackBody = base64_encode(json_encode([
'callbackUrl' => $callbackUrl,
'callbackBody' => 'filename=${object}&size=${size}&mimeType=${mimeType}&height=${imageInfo.height}&width=${imageInfo.width}',
'callbackBodyType' => "application/x-www-form-urlencoded"
]));
$policy = json_encode([
'expiration' => $this->gmtIso8601(time() + 30),
'conditions' =>
[
[0 => 'content-length-range', 1 => 0, 2 => 1048576000],
[0 => 'starts-with', 1 => '$key', 2 => $dir]
]
]);
$base64Policy = base64_encode($policy);
$signature = base64_encode(hash_hmac('sha1', $base64Policy, $this->secretKey, true));
return [
'accessid' => $this->accessKey,
'host' => $this->uploadUrl,
'cdn' => $this->cdn,
'policy' => $base64Policy,
'signature' => $signature,
'expire' => time() + 30,
'callback' => $base64CallbackBody,
'type' => 'OSS'
];
}
/**
* 获取ISO时间格式
* @param $time
* @return string
*/
protected function gmtIso8601($time)
{
$dtStr = date("c", $time);
$mydatetime = new \DateTime($dtStr);
$expiration = $mydatetime->format(\DateTime::ISO8601);
$pos = strpos($expiration, '+');
$expiration = substr($expiration, 0, $pos);
return $expiration . "Z";
}
/**
* 获取当前管理下的所有桶
* @param string|null $region
* @param bool $line
* @param bool $shared
* @return mixed|\OSS\Model\BucketListInfo
* @throws OssException
*/
public function listbuckets(string $region = 'oss-cn-hangzhou.aliyuncs.com', bool $line = false, bool $shared = false)
{
$handle = new OssClient($this->accessKey, $this->secretKey, $region);
$response = $handle->listBuckets();
$data = $response->getBucketList();
$list = [];
foreach ($data as $item) {
$list[] = [
'location' => $item->getLocation(),
'name' => $item->getName(),
'createTime' => $item->getCreateDate()
];
}
return $list;
}
/**
* @param string $name
* @param string $region
* @param string $acl
* @return mixed|void
*/
public function createBucket(string $name, string $region = '', string $acl = OssClient::OSS_ACL_TYPE_PUBLIC_READ)
{
$regionData = $this->getRegion();
if (!in_array($region, array_column($regionData, 'value'))) {
return $this->setError('OSS:无效的区域');
}
try {
$handle = new OssClient($this->accessKey, $this->secretKey, $region);
if ($handle->doesBucketExist($name)) {
return $this->setError('OSS:空间已经存在,请更换空间名');
}
return $handle->createBucket($name, $acl);
} catch (\Throwable $e) {
if (strstr('The bucket you access does not belong to you', $e->getMessage())) {
return $this->setError('OSS:空间已被使用,请更换空间名');
}
return $this->setError('OSS:' . $e->getMessage());
}
}
/**
* @param string $name
* @param string $region
* @return bool|mixed|null
*/
public function deleteBucket(string $name, string $region = '')
{
try {
$handle = new OssClient($this->accessKey, $this->secretKey, $region);
return $handle->deleteBucket($name);
} catch (\Throwable $e) {
return $this->setError($e->getMessage());
}
}
/**
* @param string $name
* @param string|null $region
* @return array|bool
*/
public function getDomian(string $name, string $region = null)
{
try {
$handle = new OssClient($this->accessKey, $this->secretKey, $region);
$res = $handle->getBucketCname($name);
$data = $res->getCnames();
return array_column($data, 'Domain');
} catch (\Throwable $e) {
}
return [];
}
/**
* 绑定域名
* @param string $name
* @param string $domain
* @param string|null $region
* @return bool|mixed
*/
public function bindDomian(string $name, string $domain, string $region = null)
{
$parseDomin = parse_url($domain);
try {
$handle = new OssClient($this->accessKey, $this->secretKey, $region);
$res = $handle->getBucketCname($name);
$data = $res->getCnames();
if (in_array($parseDomin['host'], array_column($data, 'Domain'))) {
return true;
}
return $handle->addBucketCname($name, $parseDomin['host']);
} catch (\Throwable $e) {
return $this->setError($e->getMessage());
}
}
/**
* 设置跨域
* @param string $name
* @param string $region
* @return bool|null
*/
public function setBucketCors(string $name, string $region)
{
try {
$handle = new OssClient($this->accessKey, $this->secretKey, $region);
$corsConfig = new CorsConfig();
$rule = new CorsRule();
// 设置允许跨域请求的响应头。AllowedHeader可以设置多个每个AllowedHeader中最多只能使用一个通配符星号*)。
// 建议无特殊需求时设置AllowedHeader为星号*)。
$rule->addAllowedHeader("*");
// 设置允许用户从应用程序中访问的响应头。ExposeHeader可以设置多个ExposeHeader中不支持使用通配符星号*)。
$rule->addExposeHeader("ETag");
// 设置允许的跨域请求的来源。AllowedOrigin可以设置多个每个AllowedOrigin中最多只能使用一个通配符星号*)。
// 设置AllowedOrigin为星号*)时,表示允许所有域的来源。
$rule->addAllowedOrigin("*");
// 设置允许的跨域请求方法。
$rule->addAllowedMethod("POST");
$rule->addAllowedMethod("GET");
$rule->addAllowedMethod("DELETE");
$rule->addAllowedMethod("PUT");
$rule->addAllowedMethod("HEAD");
// 设置浏览器对特定资源的预取OPTIONS请求返回结果的缓存时间单位为秒。
$rule->setMaxAgeSeconds(600);
// 每个Bucket最多支持添加10条规则。
$corsConfig->addRule($rule);
return $handle->putBucketCors($name, $corsConfig);
} catch (\Throwable $e) {
return $this->setError($e->getMessage());
}
}
/**
* 数据中心
* @return mixed|\string[][]
*/
public function getRegion()
{
return [
[
'value' => 'oss-cn-hangzhou.aliyuncs.com',
'label' => '华东1杭州'
],
[
'value' => 'oss-cn-shanghai.aliyuncs.com',
'label' => '华东2上海'
],
[
'value' => 'oss-cn-qingdao.aliyuncs.com',
'label' => '华北1青岛'
],
[
'value' => 'oss-cn-beijing.aliyuncs.com',
'label' => '华北2北京'
],
[
'value' => 'oss-cn-zhangjiakou.aliyuncs.com',
'label' => '华北 3张家口'
],
[
'value' => 'oss-cn-huhehaote.aliyuncs.com',
'label' => '华北5呼和浩特'
],
[
'value' => 'oss-cn-wulanchabu.aliyuncs.com',
'label' => '华北6乌兰察布'
],
[
'value' => 'oss-cn-shenzhen.aliyuncs.com',
'label' => '华南1深圳'
],
[
'value' => 'oss-cn-heyuan.aliyuncs.com',
'label' => '华南2河源'
],
[
'value' => 'oss-cn-guangzhou.aliyuncs.com',
'label' => '华南3广州'
],
[
'value' => 'oss-cn-chengdu.aliyuncs.com',
'label' => '西南1成都'
],
[
'value' => 'oss-cn-hongkong.aliyuncs.com',
'label' => '中国(香港)'
],
[
'value' => 'oss-us-west-1.aliyuncs.com',
'label' => '美国(硅谷)*'
],
[
'value' => 'oss-us-east-1.aliyuncs.com',
'label' => '美国(弗吉尼亚)*'
],
[
'value' => 'oss-ap-southeast-1.aliyuncs.com',
'label' => '新加坡*'
],
[
'value' => 'oss-ap-southeast-2.aliyuncs.com',
'label' => '澳大利亚(悉尼)*'
],
[
'value' => 'oss-ap-southeast-3.aliyuncs.com',
'label' => '马来西亚(吉隆坡)*'
],
[
'value' => 'oss-ap-southeast-5.aliyuncs.com',
'label' => '印度尼西亚(雅加达)*'
],
[
'value' => 'oss-ap-northeast-1.aliyuncs.com',
'label' => '日本(东京)*'
],
[
'value' => 'oss-ap-south-1.aliyuncs.com',
'label' => '印度(孟买)*'
],
[
'value' => 'oss-eu-central-1.aliyuncs.com',
'label' => '德国(法兰克福)*'
],
[
'value' => 'oss-eu-west-1.aliyuncs.com',
'label' => '英国(伦敦)'
],
[
'value' => 'oss-me-east-1.aliyuncs.com',
'label' => '阿联酋(迪拜)*'
],
[
'value' => 'oss-ap-southeast-6.aliyuncs.com',
'label' => '菲律宾(马尼拉)'
]
];
}
}