null, 'before_upload' => null, 'concurrency' => 5, 'mup_threshold' => self::DEFAULT_MULTIPART_THRESHOLD, 'params' => [], 'part_size' => null, 'version_id' => null, ]; /** * @param S3ClientInterface $client The S3 Client used to execute * the copy command(s). * @param array $source The object to copy, specified as * an array with a 'Bucket' and * 'Key' keys. Provide a * 'VersionID' key to copy a * specified version of an object. * @param array $destination The bucket and key to which to * copy the $source, specified as * an array with a 'Bucket' and * 'Key' keys. * @param string $acl ACL to apply to the copy * (default: private). * @param array $options Options used to configure the * copy process. Options passed in * through 'params' are added to * the sub commands. * * @throws InvalidArgumentException */ public function __construct( S3ClientInterface $client, array $source, array $destination, $acl = 'private', array $options = [] ) { $this->validateLocation($source); $this->validateLocation($destination); $this->client = $client; $this->source = $source; $this->destination = $destination; $this->acl = $acl; $this->options = $options + self::$defaults; } /** * Perform the configured copy asynchronously. Returns a promise that is * fulfilled with the result of the CompleteMultipartUpload or CopyObject * operation or rejected with an exception. * * @return Coroutine */ public function promise() { return Coroutine::of(function () { $headObjectCommand = $this->client->getCommand( 'HeadObject', $this->options['params'] + $this->source ); if (is_callable($this->options['before_lookup'])) { $this->options['before_lookup']($headObjectCommand); } $objectStats = (yield $this->client->executeAsync( $headObjectCommand )); if ($objectStats['ContentLength'] > $this->options['mup_threshold']) { $mup = new MultipartCopy( $this->client, $this->getSourcePath(), ['source_metadata' => $objectStats, 'acl' => $this->acl] + $this->destination + $this->options ); yield $mup->promise(); } else { $defaults = [ 'ACL' => $this->acl, 'MetadataDirective' => 'COPY', 'CopySource' => $this->getSourcePath(), ]; $params = array_diff_key($this->options, self::$defaults) + $this->destination + $defaults + $this->options['params']; yield $this->client->executeAsync( $this->client->getCommand('CopyObject', $params) ); } }); } /** * Perform the configured copy synchronously. Returns the result of the * CompleteMultipartUpload or CopyObject operation. * * @return Result * * @throws S3Exception * @throws MultipartUploadException */ public function copy() { return $this->promise()->wait(); } private function validateLocation(array $location) { if (empty($location['Bucket']) || empty($location['Key'])) { throw new \InvalidArgumentException('Locations provided to an' . ' Aws\S3\ObjectCopier must have a non-empty Bucket and Key'); } } private function getSourcePath() { if (ArnParser::isArn($this->source['Bucket'])) { try { new AccessPointArn($this->source['Bucket']); } catch (\Exception $e) { throw new \InvalidArgumentException( 'Provided ARN was a not a valid S3 access point ARN (' . $e->getMessage() . ')', 0, $e ); } } $sourcePath = "/{$this->source['Bucket']}/" . rawurlencode($this->source['Key']); if (isset($this->source['VersionId'])) { $sourcePath .= "?versionId={$this->source['VersionId']}"; } return $sourcePath; } }