Вход Регистрация
Файл: includes/library/aws/Aws/S3/S3Client.php
Строк: 652
<?php
namespace AwsS3;

use 
AwsApiApiProvider;
use 
AwsApiDocModel;
use 
AwsApiService;
use 
AwsAwsClient;
use 
AwsClientResolver;
use 
AwsCommand;
use 
AwsExceptionAwsException;
use 
AwsHandlerList;
use 
AwsMiddleware;
use 
AwsRetryMiddleware;
use 
AwsS3ExceptionS3Exception;
use 
AwsResultInterface;
use 
AwsCommandInterface;
use 
GuzzleHttpPsr7;
use 
PsrHttpMessageRequestInterface;
use 
PsrHttpMessageStreamInterface;

/**
 * Client used to interact with **Amazon Simple Storage Service (Amazon S3)**.
 */
class S3Client extends AwsClient
{
    public static function 
getArguments()
    {
        
$args parent::getArguments();
        
$args['retries']['fn'] = [__CLASS__'_applyRetryConfig'];
        
$args['api_provider']['fn'] = [__CLASS__'_applyApiProvider'];

        return 
$args + [
            
'bucket_endpoint' => [
                
'type'    => 'config',
                
'valid'   => ['bool'],
                
'doc'     => 'Set to true to send requests to a hardcoded '
                    
'bucket endpoint rather than create an endpoint as a '
                    
'result of injecting the bucket into the URL. This '
                    
'option is useful for interacting with CNAME endpoints.',
            ],
        ];
    }

    
/**
     * {@inheritdoc}
     *
     * In addition to the options available to
     * {@see AwsAwsClient::__construct}, S3Client accepts the following
     * options:
     *
     * - bucket_endpoint: (bool) Set to true to send requests to a
     *   hardcoded bucket endpoint rather than create an endpoint as a result
     *   of injecting the bucket into the URL. This option is useful for
     *   interacting with CNAME endpoints.
     * - calculate_md5: (bool) Set to false to disable calculating an MD5
     *   for all Amazon S3 signed uploads.
     *
     * @param array $args
     */
    
public function __construct(array $args)
    {
        
parent::__construct($args);
        
$stack $this->getHandlerList();
        
$stack->appendInit(SSECMiddleware::wrap($this->getEndpoint()->getScheme()), 's3.ssec');
        
$stack->appendBuild(ApplyMd5Middleware::wrap(), 's3.md5');
        
$stack->appendBuild(
            
Middleware::contentType(['PutObject''UploadPart']),
            
's3.content_type'
        
);

        
// Use the bucket style middleware when using a "bucket_endpoint" (for cnames)
        
if ($this->getConfig('bucket_endpoint')) {
            
$stack->appendBuild(BucketEndpointMiddleware::wrap(), 's3.bucket_endpoint');
        }

        
$stack->appendSign(PutObjectUrlMiddleware::wrap(), 's3.put_object_url');
        
$stack->appendSign(PermanentRedirectMiddleware::wrap(), 's3.permanent_redirect');
        
$stack->appendInit(Middleware::sourceFile($this->getApi()), 's3.source_file');
        
$stack->appendInit($this->getSaveAsParameter(), 's3.save_as');
        
$stack->appendInit($this->getLocationConstraintMiddleware(), 's3.location');
    }

    
/**
     * Determine if a string is a valid name for a DNS compatible Amazon S3
     * bucket.
     *
     * DNS compatible bucket names can be used as a subdomain in a URL (e.g.,
     * "<bucket>.s3.amazonaws.com").
     *
     * @param string $bucket Bucket name to check.
     *
     * @return bool
     */
    
public static function isBucketDnsCompatible($bucket)
    {
        
$bucketLen strlen($bucket);

        return (
$bucketLen >= && $bucketLen <= 63) &&
            
// Cannot look like an IP address
            
!filter_var($bucketFILTER_VALIDATE_IP) &&
            
preg_match('/^[a-z0-9]([a-z0-9-.]*[a-z0-9])?$/'$bucket);
    }

    
/**
     * Create a pre-signed URL for the given S3 command object.
     *
     * @param CommandInterface $command     Command to create a pre-signed
     *                                      URL for.
     * @param int|string|DateTime $expires The time at which the URL should
     *                                      expire. This can be a Unix
     *                                      timestamp, a PHP DateTime object,
     *                                      or a string that can be evaluated
     *                                      by strtotime().
     *
     * @return RequestInterface
     */
    
public function createPresignedRequest(CommandInterface $command$expires)
    {
        
/** @var AwsSignatureSignatureInterface $signer */
        
$signer call_user_func(
            
$this->getSignatureProvider(),
            
$this->getConfig('signature_version'),
            
$this->getApi()->getSigningName(),
            
$this->getRegion()
        );

        return 
$signer->presign(
            
Awsserialize($command),
            
$this->getCredentials()->wait(),
            
$expires
        
);
    }

    
/**
     * Returns the URL to an object identified by its bucket and key.
     *
     * The URL returned by this method is not signed nor does it ensure the the
     * bucket and key given to the method exist. If you need a signed URL, then
     * use the {@see AwsS3S3Client::createPresignedRequest} method and get
     * the URI of the signed request.
     *
     * @param string $bucket  The name of the bucket where the object is located
     * @param string $key     The key of the object
     *
     * @return string The URL to the object
     */
    
public function getObjectUrl($bucket$key)
    {
        
$command $this->getCommand('GetObject', [
            
'Bucket' => $bucket,
            
'Key'    => $key
        
]);

        return (string) 
Awsserialize($command)->getUri();
    }

    
/**
     * Determines whether or not a bucket exists by name.
     *
     * @param string $bucket  The name of the bucket
     *
     * @return bool
     */
    
public function doesBucketExist($bucket)
    {
        return 
$this->checkExistenceWithCommand(
            
$this->getCommand('HeadBucket', ['Bucket' => $bucket])
        );
    }

    
/**
     * Determines whether or not an object exists by name.
     *
     * @param string $bucket  The name of the bucket
     * @param string $key     The key of the object
     * @param array  $options Additional options available in the HeadObject
     *                        operation (e.g., VersionId).
     *
     * @return bool
     */
    
public function doesObjectExist($bucket$key, array $options = [])
    {
        return 
$this->checkExistenceWithCommand(
            
$this->getCommand('HeadObject', [
                
'Bucket' => $bucket,
                
'Key'    => $key
            
] + $options)
        );
    }

    
/**
     * Raw URL encode a key and allow for '/' characters
     *
     * @param string $key Key to encode
     *
     * @return string Returns the encoded key
     */
    
public static function encodeKey($key)
    {
        return 
str_replace('%2F''/'rawurlencode($key));
    }

    
/**
     * Register the Amazon S3 stream wrapper with this client instance.
     */
    
public function registerStreamWrapper()
    {
        
StreamWrapper::register($this);
    }

    
/**
     * Deletes objects from Amazon S3 that match the result of a ListObjects
     * operation. For example, this allows you to do things like delete all
     * objects that match a specific key prefix.
     *
     * @param string $bucket  Bucket that contains the object keys
     * @param string $prefix  Optionally delete only objects under this key prefix
     * @param string $regex   Delete only objects that match this regex
     * @param array  $options AwsS3BatchDelete options array.
     *
     * @see AwsS3S3Client::listObjects
     * @throws RuntimeException if no prefix and no regex is given
     */
    
public function deleteMatchingObjects(
        
$bucket,
        
$prefix '',
        
$regex '',
        array 
$options = []
    ) {
        if (!
$prefix && !$regex) {
            throw new 
RuntimeException('A prefix or regex is required.');
        }

        
$params = ['Bucket' => $bucket'Prefix' => $prefix];
        
$iter $this->getIterator('ListObjects'$params);

        if (
$regex) {
            
$iter Awsfilter($iter, function ($c) use ($regex) {
                return 
preg_match($regex$c['Key']);
            });
        }

        
BatchDelete::fromIterator($this$bucket$iter$options)->delete();
    }

    
/**
     * Upload a file, stream, or string to a bucket.
     *
     * If the upload size exceeds the specified threshold, the upload will be
     * performed using concurrent multipart uploads.
     *
     * The options array accepts the following options:
     *
     * - before_upload: (callable) Callback to invoke before any upload
     *   operations during the upload process. The callback should have a
     *   function signature like `function (AwsCommand $command) {...}`.
     * - concurrency: (int, default=int(3)) Maximum number of concurrent
     *   `UploadPart` operations allowed during a multipart upload.
     * - mup_threshold: (int, default=int(16777216)) The size, in bytes, allowed
     *   before the upload must be sent via a multipart upload. Default: 16 MB.
     * - params: (array, default=array([])) Custom parameters to use with the
     *   upload. For single uploads, they must correspond to those used for the
     *   `PutObject` operation. For multipart uploads, they correspond to the
     *   parameters of the `CreateMultipartUpload` operation.
     * - part_size: (int) Part size to use when doing a multipart upload.
     *
     * @param string $bucket  Bucket to upload the object.
     * @param string $key     Key of the object.
     * @param mixed  $body    Object data to upload. Can be a
     *                        StreamInterface, PHP stream resource, or a
     *                        string of data to upload.
     * @param string $acl     ACL to apply to the object (default: private).
     * @param array  $options Options used to configure the upload process.
     *
     * @see AwsS3MultipartUploader for more info about multipart uploads.
     * @return ResultInterface Returns the result of the upload.
     */
    
public function upload($bucket$key$body$acl 'private', array $options = [])
    {
        
// Prepare the options.
        
static $defaults = [
            
'before_upload' => null,
            
'concurrency'   => 3,
            
'mup_threshold' => 16777216,
            
'params'        => [],
            
'part_size'     => null,
        ];
        
$options array_intersect_key($options $defaults$defaults);

        
// Perform the required operations to upload the S3 Object.
        
$body Psr7stream_for($body);
        if (
$this->requiresMultipart($body$options['mup_threshold'])) {
            
// Perform a multipart upload.
            
$options['before_initiate'] = function ($command) use ($options) {
                foreach (
$options['params'] as $k => $v) {
                    
$command[$k] = $v;
                }
            };
            return (new 
MultipartUploader($this$body, [
                
'bucket' => $bucket,
                
'key'    => $key,
                
'acl'    => $acl
            
] + $options))->upload();
        } else {
            
// Perform a regular PutObject operation.
            
$command $this->getCommand('PutObject', [
                
'Bucket' => $bucket,
                
'Key'    => $key,
                
'Body'   => $body,
                
'ACL'    => $acl,
            ] + 
$options['params']);
            if (
is_callable($options['before_upload'])) {
                
$options['before_upload']($command);
            }
            return 
$this->execute($command);
        }
    }

    
/**
     * Recursively uploads all files in a given directory to a given bucket.
     *
     * @param string $directory Full path to a directory to upload
     * @param string $bucket    Name of the bucket
     * @param string $keyPrefix Virtual directory key prefix to add to each upload
     * @param array  $options   Options available in AwsS3Transfer::__construct
     *
     * @see AwsS3Transfer for more options and customization
     */
    
public function uploadDirectory(
        
$directory,
        
$bucket,
        
$keyPrefix null,
        array 
$options = []
    ) {
        
$d "s3://$bucket. ($keyPrefix '/' ltrim($keyPrefix'/') : '');
        (new 
Transfer($this$directory$d$options))->transfer();
    }

    
/**
     * Downloads a bucket to the local filesystem
     *
     * @param string $directory Directory to download to
     * @param string $bucket    Bucket to download from
     * @param string $keyPrefix Only download objects that use this key prefix
     * @param array  $options   Options available in AwsS3Transfer::__construct
     */
    
public function downloadBucket(
        
$directory,
        
$bucket,
        
$keyPrefix '',
        array 
$options = []
    ) {
        
$s "s3://$bucket. ($keyPrefix '/' ltrim($keyPrefix'/') : '');
        (new 
Transfer($this$s$directory$options))->transfer();
    }

    
/**
     * Determines if the body should be uploaded using PutObject or the
     * Multipart Upload System. It also modifies the passed-in $body as needed
     * to support the upload.
     *
     * @param StreamInterface $body      Stream representing the body.
     * @param integer             $threshold Minimum bytes before using Multipart.
     *
     * @return bool
     */
    
private function requiresMultipart(StreamInterface &$body$threshold)
    {
        
// If body size known, compare to threshold to determine if Multipart.
        
if ($body->getSize() !== null) {
            return 
$body->getSize() >= $threshold;
        }

        
// Handle the situation where the body size is unknown.
        // Read up to 5MB into a buffer to determine how to upload the body.
        
$buffer Psr7stream_for();
        
Psr7copy_to_stream($body$bufferMultipartUploader::PART_MIN_SIZE);

        
// If body < 5MB, use PutObject with the buffer.
        
if ($buffer->getSize() < MultipartUploader::PART_MIN_SIZE) {
            
$buffer->seek(0);
            
$body $buffer;
            return 
false;
        }

        
// If body >= 5 MB, then use multipart. [YES]
        
if ($body->isSeekable()) {
            
// If the body is seekable, just rewind the body.
            
$body->seek(0);
        } else {
            
// If the body is non-seekable, stitch the rewind the buffer and
            // the partially read body together into one stream. This avoids
            // unnecessary disc usage and does not require seeking on the
            // original stream.
            
$buffer->seek(0);
            
$body = new Psr7AppendStream([$buffer$body]);
        }

        return 
true;
    }

    
/**
     * Determines whether or not a resource exists using a command
     *
     * @param CommandInterface $command Command used to poll for the resource
     *
     * @return bool
     * @throws S3Exception|Exception if there is an unhandled exception
     */
    
private function checkExistenceWithCommand(CommandInterface $command)
    {
        try {
            
$this->execute($command);
            return 
true;
        } catch (
S3Exception $e) {
            if (
$e->getAwsErrorCode() == 'AccessDenied') {
                return 
true;
            }
            if (
$e->getStatusCode() >= 500) {
                throw 
$e;
            }
            return 
false;
        }
    }

    
/**
     * Provides a middleware that removes the need to specify LocationConstraint on CreateBucket.
     *
     * @return Closure
     */
    
private function getLocationConstraintMiddleware()
    {
        return function (callable 
$handler) {
            return function (
Command $command$request null) use ($handler) {
                if (
$command->getName() === 'CreateBucket') {
                    
$region $this->getRegion();
                    if (
$region === 'us-east-1') {
                        unset(
$command['CreateBucketConfiguration']);
                    } else {
                        
$command['CreateBucketConfiguration'] = ['LocationConstraint' => $region];
                    }
                }

                return 
$handler($command$request);
            };
        };
    }

    
/**
     * Provides a middleware that supports the `SaveAs` parameter.
     *
     * @return Closure
     */
    
private function getSaveAsParameter()
    {
        return function (callable 
$handler) {
            return function (
Command $command$request null) use ($handler) {
                if (
$command->getName() === 'GetObject' && isset($command['SaveAs'])) {
                    
$command['@http']['sink'] = $command['SaveAs'];
                    unset(
$command['SaveAs']);
                }

                return 
$handler($command$request);
            };
        };
    }

    
/** @internal */
    
public static function _applyRetryConfig($value$_HandlerList $list)
    {
        if (!
$value) {
            return;
        }

        
$decider RetryMiddleware::createDefaultDecider($value);
        
$decider = function ($retries$request$result$error) use ($decider) {
            if (
$decider($retries$request$result$error)) {
                return 
true;
            } elseif (
$error instanceof AwsException) {
                return 
$error->getResponse()
                    && 
strpos(
                        
$error->getResponse()->getBody(),
                        
'Your socket connection to the server'
                    
) !== false;
            }
            return 
false;
        };

        
$delay = [RetryMiddleware::class, 'exponentialDelay'];
        
$list->appendSign(Middleware::retry($decider$delay), 'retry');
    }

    
/** @internal */
    
public static function _applyApiProvider($value, array &$argsHandlerList $list)
    {
        
ClientResolver::_apply_api_provider($value$args$list);
        
$args['parser'] = new GetBucketLocationParser($args['parser']);
    }

    
/**
     * @internal
     * @codeCoverageIgnore
     */
    
public static function applyDocFilters(array $api, array $docs)
    {
        
$b64 '<div class="alert alert-info">This value will be base64 '
            
'encoded on your behalf.</div>';

        
// Add the SourceFile parameter.
        
$docs['shapes']['SourceFile']['base'] = 'The path to a file on disk to use instead of the Body parameter.';
        
$api['shapes']['SourceFile'] = ['type' => 'string'];
        
$api['shapes']['PutObjectRequest']['members']['SourceFile'] = ['shape' => 'SourceFile'];
        
$api['shapes']['UploadPartRequest']['members']['SourceFile'] = ['shape' => 'SourceFile'];

        
// Add the SaveAs parameter.
        
$docs['shapes']['SaveAs']['base'] = 'The path to a file on disk to save the object data.';
        
$api['shapes']['SaveAs'] = ['type' => 'string'];
        
$api['shapes']['GetObjectRequest']['members']['SaveAs'] = ['shape' => 'SaveAs'];

        
// Several SSECustomerKey documentation updates.
        
$docs['shapes']['SSECustomerKey']['append'] = $b64;
        
$docs['shapes']['CopySourceSSECustomerKey']['append'] = $b64;
        
$docs['shapes']['SSECustomerKeyMd5']['append'] = '<div class="alert alert-info">The value will be computed on '
            
'your behalf if it is not supplied.</div>';

        
// Add the ObjectURL to various output shapes and documentation.
        
$docs['shapes']['ObjectURL']['base'] = 'The URI of the created object.';
        
$api['shapes']['ObjectURL'] = ['type' => 'string'];
        
$api['shapes']['PutObjectOutput']['members']['ObjectURL'] = ['shape' => 'ObjectURL'];
        
$api['shapes']['CopyObjectOutput']['members']['ObjectURL'] = ['shape' => 'ObjectURL'];
        
$api['shapes']['CompleteMultipartUploadOutput']['members']['ObjectURL'] = ['shape' => 'ObjectURL'];

        
// Fix references to Location Constraint.
        
unset($api['shapes']['CreateBucketRequest']['payload']);
        
$api['shapes']['BucketLocationConstraint']['enum'] = [
            
"ap-northeast-1",
            
"ap-southeast-2",
            
"ap-southeast-1",
            
"cn-north-1",
            
"eu-central-1",
            
"eu-west-1",
            
"us-east-1",
            
"us-west-1",
            
"us-west-2",
            
"sa-east-1",
        ];

        
// Add a note that the ContentMD5 is optional.
        
$docs['shapes']['ContentMD5']['append'] = '<div class="alert alert-info">The value will be computed on '
            
'your behalf.</div>';

        return [
            new 
Service($apiApiProvider::defaultProvider()),
            new 
DocModel($docs)
        ];
    }
}
Онлайн: 0
Реклама