ObjectCopier.php 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. <?php
  2. namespace Aws\S3;
  3. use Aws\Exception\MultipartUploadException;
  4. use Aws\Result;
  5. use Aws\S3\Exception\S3Exception;
  6. use GuzzleHttp\Promise\PromisorInterface;
  7. use InvalidArgumentException;
  8. /**
  9. * Copies objects from one S3 location to another, utilizing a multipart copy
  10. * when appropriate.
  11. */
  12. class ObjectCopier implements PromisorInterface
  13. {
  14. const DEFAULT_MULTIPART_THRESHOLD = MultipartUploader::PART_MAX_SIZE;
  15. private $client;
  16. private $source;
  17. private $destination;
  18. private $acl;
  19. private $options;
  20. private static $defaults = [
  21. 'before_lookup' => null,
  22. 'before_upload' => null,
  23. 'concurrency' => 5,
  24. 'mup_threshold' => self::DEFAULT_MULTIPART_THRESHOLD,
  25. 'params' => [],
  26. 'part_size' => null,
  27. 'version_id' => null,
  28. ];
  29. /**
  30. * @param S3ClientInterface $client The S3 Client used to execute
  31. * the copy command(s).
  32. * @param array $source The object to copy, specified as
  33. * an array with a 'Bucket' and
  34. * 'Key' keys. Provide a
  35. * 'VersionID' key to copy a
  36. * specified version of an object.
  37. * @param array $destination The bucket and key to which to
  38. * copy the $source, specified as
  39. * an array with a 'Bucket' and
  40. * 'Key' keys.
  41. * @param string $acl ACL to apply to the copy
  42. * (default: private).
  43. * @param array $options Options used to configure the
  44. * copy process. Options passed in
  45. * through 'params' are added to
  46. * the sub commands.
  47. *
  48. * @throws InvalidArgumentException
  49. */
  50. public function __construct(
  51. S3ClientInterface $client,
  52. array $source,
  53. array $destination,
  54. $acl = 'private',
  55. array $options = []
  56. ) {
  57. $this->validateLocation($source);
  58. $this->validateLocation($destination);
  59. $this->client = $client;
  60. $this->source = $source;
  61. $this->destination = $destination;
  62. $this->acl = $acl;
  63. $this->options = $options + self::$defaults;
  64. }
  65. /**
  66. * Perform the configured copy asynchronously. Returns a promise that is
  67. * fulfilled with the result of the CompleteMultipartUpload or CopyObject
  68. * operation or rejected with an exception.
  69. */
  70. public function promise()
  71. {
  72. return \GuzzleHttp\Promise\coroutine(function () {
  73. $headObjectCommand = $this->client->getCommand(
  74. 'HeadObject',
  75. $this->options['params'] + $this->source
  76. );
  77. if (is_callable($this->options['before_lookup'])) {
  78. $this->options['before_lookup']($headObjectCommand);
  79. }
  80. $objectStats = (yield $this->client->executeAsync(
  81. $headObjectCommand
  82. ));
  83. if ($objectStats['ContentLength'] > $this->options['mup_threshold']) {
  84. $mup = new MultipartCopy(
  85. $this->client,
  86. $this->getSourcePath(),
  87. ['source_metadata' => $objectStats, 'acl' => $this->acl]
  88. + $this->destination
  89. + $this->options
  90. );
  91. yield $mup->promise();
  92. } else {
  93. $defaults = [
  94. 'ACL' => $this->acl,
  95. 'MetadataDirective' => 'COPY',
  96. 'CopySource' => $this->getSourcePath(),
  97. ];
  98. $params = array_diff_key($this->options, self::$defaults)
  99. + $this->destination + $defaults + $this->options['params'];
  100. yield $this->client->executeAsync(
  101. $this->client->getCommand('CopyObject', $params)
  102. );
  103. }
  104. });
  105. }
  106. /**
  107. * Perform the configured copy synchronously. Returns the result of the
  108. * CompleteMultipartUpload or CopyObject operation.
  109. *
  110. * @return Result
  111. *
  112. * @throws S3Exception
  113. * @throws MultipartUploadException
  114. */
  115. public function copy()
  116. {
  117. return $this->promise()->wait();
  118. }
  119. private function validateLocation(array $location)
  120. {
  121. if (empty($location['Bucket']) || empty($location['Key'])) {
  122. throw new \InvalidArgumentException('Locations provided to an'
  123. . ' Aws\S3\ObjectCopier must have a non-empty Bucket and Key');
  124. }
  125. }
  126. private function getSourcePath()
  127. {
  128. $sourcePath = "/{$this->source['Bucket']}/"
  129. . rawurlencode($this->source['Key']);
  130. if (isset($this->source['VersionId'])) {
  131. $sourcePath .= "?versionId={$this->source['VersionId']}";
  132. }
  133. return $sourcePath;
  134. }
  135. }