admin/vendor/overtrue/flysystem-cos/src/CosAdapter.php

561 lines
13 KiB
PHP

<?php
/*
* This file is part of the overtrue/flysystem-cos.
* (c) overtrue <i@overtrue.me>
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Overtrue\Flysystem\Cos;
use GuzzleHttp\Client as HttpClient;
use League\Flysystem\Adapter\AbstractAdapter;
use League\Flysystem\Adapter\CanOverwriteFiles;
use League\Flysystem\AdapterInterface;
use League\Flysystem\Config;
use Qcloud\Cos\Client;
use Qcloud\Cos\Exception\NoSuchKeyException;
/**
* Class CosAdapter.
*/
class CosAdapter extends AbstractAdapter implements CanOverwriteFiles
{
/**
* @var Client
*/
protected $client;
/**
* @var \GuzzleHttp\Client
*/
protected $httpClient;
/**
* @var array
*/
protected $config;
/**
* CosAdapter constructor.
*
* @param array $config
*/
public function __construct(array $config = [])
{
$this->config = $config;
if (!empty($this->config['cdn'])) {
$this->setPathPrefix($this->config['cdn']);
}
}
/**
* @return string
*/
public function getBucket()
{
return $this->config['bucket'];
}
/**
* @return string
*/
public function getAppId()
{
return $this->config['credentials']['appId'] ?? null;
}
/**
* @return string
*/
public function getRegion()
{
return $this->config['region'] ?? '';
}
/**
* @param string $path
*
* @return string
*/
public function getSourcePath($path)
{
return sprintf('%s.cos.%s.myqcloud.com/%s',
$this->getBucket(), $this->getRegion(), $path
);
}
/**
* @param string $path
*
* @return string
*/
public function getUrl($path)
{
if (!empty($this->config['cdn'])) {
return $this->applyPathPrefix($path);
}
$options = [
'Scheme' => $this->config['scheme'] ?? 'http',
];
return $this->getClient()->getObjectUrl(
$this->getBucket(), $path, null, $options
);
}
/**
* @param string $path
* @param string|int $expiration
* @param array $options
*
* @return string
*/
public function getTemporaryUrl($path, $expiration, array $options = [])
{
$options = array_merge($options, ['Scheme' => $this->config['scheme'] ?? 'http']);
$expiration = date('c', !\is_numeric($expiration) ? \strtotime($expiration) : \intval($expiration));
$objectUrl = $this->getClient()->getObjectUrl(
$this->getBucket(), $path, $expiration, $options
);
$url = parse_url($objectUrl);
if (!empty($this->config['cdn'])) {
return \sprintf(
'%s/%s?%s',
\rtrim($this->config['cdn'], '/'),
\ltrim(urldecode($url['path']), '/'),
$url['query']
);
}
return $objectUrl;
}
/**
* @param string $path
* @param string $contents
* @param Config $config
*
* @return array|false
*/
public function write($path, $contents, Config $config)
{
$options = $this->getUploadOptions($config);
return $this->getClient()->upload($this->getBucket(), $path, $contents, $options['params']);
}
/**
* @param string $path
* @param resource $resource
* @param Config $config
*
* @return array|false
*/
public function writeStream($path, $resource, Config $config)
{
$options = $this->getUploadOptions($config);
return $this->getClient()->upload(
$this->getBucket(),
$path,
stream_get_contents($resource, -1, 0),
$options['params']
);
}
/**
* @param string $path
* @param string $contents
* @param Config $config
*
* @return array|bool
*/
public function update($path, $contents, Config $config)
{
return $this->write($path, $contents, $config);
}
/**
* @param string $path
* @param resource $resource
* @param Config $config
*
* @return array|bool
*/
public function updateStream($path, $resource, Config $config)
{
return $this->writeStream($path, $resource, $config);
}
/**
* @param string $path
* @param string $to
*
* @return bool
*/
public function rename($path, $to)
{
$result = $this->copy($path, $to);
$this->delete($path);
return $result;
}
/**
* @param string $path
* @param string $to
*
* @return bool
*/
public function copy($path, $to)
{
$source = [
'Region' => $this->getRegion(),
'Bucket' => $this->getBucket(),
'Key' => $path,
];
return (bool) $this->getClient()->copy($this->getBucket(), $to, $source);
}
/**
* @param string $path
*
* @return bool
*/
public function delete($path)
{
return (bool) $this->getClient()->deleteObject([
'Bucket' => $this->getBucket(),
'Key' => $path,
]);
}
/**
* @param string $dirname
*
* @return bool
*/
public function deleteDir($dirname)
{
$response = $this->listObjects($dirname);
if (empty($response['Contents'])) {
return true;
}
$keys = array_map(function ($item) {
return ['Key' => $item['Key']];
}, (array) $response['Contents']);
return (bool) $this->getClient()->deleteObjects([
'Bucket' => $this->getBucket(),
'Objects' => $keys,
]);
}
/**
* @param string $dirname
* @param Config $config
*
* @return array|bool
*/
public function createDir($dirname, Config $config)
{
return $this->getClient()->putObject([
'Bucket' => $this->getBucket(),
'Key' => $dirname.'/',
'Body' => '',
]);
}
/**
* @param string $path
* @param string $visibility
*
* @return array|false
*/
public function setVisibility($path, $visibility)
{
return (bool) $this->getClient()->PutObjectAcl([
'Bucket' => $this->getBucket(),
'Key' => $path,
'ACL' => $this->normalizeVisibility($visibility),
]);
}
/**
* @param string $path
*
* @return bool
*/
public function has($path)
{
try {
return (bool) $this->getMetadata($path);
} catch (\Throwable $e) {
return false;
} catch (\Exception $e) {
return false;
}
}
/**
* @param string $path
*
* @return array|bool
*/
public function read($path)
{
try {
if ($this->config['read_from_cdn']) {
$response = $this->getHttpClient()
->get($this->getTemporaryUrl($path, date('+5 min')))
->getBody()
->getContents();
} else {
$response = $this->getClient()->getObject([
'Bucket' => $this->getBucket(),
'Key' => $path,
])['Body'];
}
return ['contents' => (string) $response];
} catch (\Throwable $e) {
return null;
} catch (\Exception $e) {
return null;
}
}
public function getClient()
{
return $this->client ?: $this->client = new Client($this->config);
}
/**
* @param \Qcloud\Cos\Client $client
*
* @return $this
*/
public function setClient(Client $client)
{
$this->client = $client;
return $this;
}
/**
* @return \GuzzleHttp\Client
*/
public function getHttpClient()
{
return $this->httpClient ?: $this->httpClient = new HttpClient();
}
/**
* @param \GuzzleHttp\Client $client
*
* @return $this
*/
public function setHttpClient(HttpClient $client)
{
$this->httpClient = $client;
return $this;
}
/**
* @param string $path
*
* @return array|bool
*/
public function readStream($path)
{
try {
$temporaryUrl = $this->getTemporaryUrl($path, \strtotime('+5 min'));
$stream = $this->getHttpClient()
->get($temporaryUrl, ['stream' => true])
->getBody()
->detach();
return ['stream' => $stream];
} catch (\Throwable $e) {
return false;
} catch (\Exception $e) {
return false;
}
}
/**
* @param string $directory
* @param bool $recursive
*
* @return array|bool
*/
public function listContents($directory = '', $recursive = false)
{
$list = [];
$response = $this->listObjects($directory, $recursive);
foreach ((array) $response['Contents'] as $content) {
$list[] = $this->normalizeFileInfo($content);
}
return $list;
}
/**
* @param string $path
*
* @return array|bool
*/
public function getMetadata($path)
{
return $this->getClient()->headObject([
'Bucket' => $this->getBucket(),
'Key' => $path,
]);
}
/**
* @param string $path
*
* @return array|bool
*/
public function getSize($path)
{
$meta = $this->getMetadata($path);
return isset($meta['ContentLength']) ? ['size' => $meta['ContentLength']] : false;
}
/**
* @param string $path
*
* @return array|bool
*/
public function getMimetype($path)
{
$meta = $this->getMetadata($path);
return isset($meta['ContentType']) ? ['mimetype' => $meta['ContentType']] : false;
}
/**
* @param string $path
*
* @return array|bool
*/
public function getTimestamp($path)
{
$meta = $this->getMetadata($path);
return isset($meta['LastModified']) ? ['timestamp' => strtotime($meta['LastModified'])] : false;
}
/**
* @param string $path
*
* @return array|bool
*/
public function getVisibility($path)
{
$meta = $this->getClient()->getObjectAcl([
'Bucket' => $this->getBucket(),
'Key' => $path,
]);
foreach ($meta->get('Grants') as $grant) {
if ('READ' === $grant['Permission'] && false !== strpos($grant['Grantee']['URI'] ?? '', 'global/AllUsers')) {
return ['visibility' => AdapterInterface::VISIBILITY_PUBLIC];
}
}
return ['visibility' => AdapterInterface::VISIBILITY_PRIVATE];
}
/**
* @param array $content
*
* @return array
*/
protected function normalizeFileInfo(array $content)
{
$path = pathinfo($content['Key']);
return [
'type' => '/' === substr($content['Key'], -1) ? 'dir' : 'file',
'path' => $content['Key'],
'size' => \intval($content['Size']),
'dirname' => \strval($path['dirname']),
'basename' => \strval($path['basename']),
'filename' => strval($path['filename']),
'timestamp' => \strtotime($content['LastModified']),
'extension' => $path['extension'] ?? '',
];
}
/**
* @param string $directory
* @param bool $recursive
*
* @return mixed
*/
protected function listObjects($directory = '', $recursive = false)
{
return $this->getClient()->listObjects([
'Bucket' => $this->getBucket(),
'Prefix' => ('' === (string) $directory) ? '' : ($directory.'/'),
'Delimiter' => $recursive ? '' : '/',
]);
}
/**
* @param Config $config
*
* @return array
*/
protected function getUploadOptions(Config $config)
{
$options = ['params' => []];
if ($config->has('params')) {
$options['params'] = (array) $config->get('params');
}
if ($config->has('visibility')) {
$options['params']['ACL'] = $this->normalizeVisibility($config->get('visibility'));
}
return $options;
}
/**
* @param $visibility
*
* @return string
*/
protected function normalizeVisibility($visibility)
{
switch ($visibility) {
case AdapterInterface::VISIBILITY_PUBLIC:
$visibility = 'public-read';
break;
}
return $visibility;
}
}