| 
<?phpnamespace Aws\S3;
 
 use Aws\HashingStream;
 use Aws\Multipart\AbstractUploader;
 use Aws\PhpHash;
 use Aws\ResultInterface;
 use GuzzleHttp\Psr7;
 use Psr\Http\Message\StreamInterface as Stream;
 
 /**
 * Encapsulates the execution of a multipart upload to S3 or Glacier.
 */
 class MultipartUploader extends AbstractUploader
 {
 use MultipartUploadingTrait;
 
 const PART_MIN_SIZE = 5242880;
 const PART_MAX_SIZE = 5368709120;
 const PART_MAX_NUM = 10000;
 
 /**
 * Creates a multipart upload for an S3 object.
 *
 * The valid configuration options are as follows:
 *
 * - acl: (string) ACL to set on the object being upload. Objects are
 *   private by default.
 * - before_complete: (callable) Callback to invoke before the
 *   `CompleteMultipartUpload` operation. The callback should have a
 *   function signature like `function (Aws\Command $command) {...}`.
 * - before_initiate: (callable) Callback to invoke before the
 *   `CreateMultipartUpload` operation. The callback should have a function
 *   signature like `function (Aws\Command $command) {...}`.
 * - before_upload: (callable) Callback to invoke before any `UploadPart`
 *   operations. The callback should have a function signature like
 *   `function (Aws\Command $command) {...}`.
 * - bucket: (string, required) Name of the bucket to which the object is
 *   being uploaded.
 * - concurrency: (int, default=int(5)) Maximum number of concurrent
 *   `UploadPart` operations allowed during the multipart upload.
 * - key: (string, required) Key to use for the object being uploaded.
 * - part_size: (int, default=int(5242880)) Part size, in bytes, to use when
 *   doing a multipart upload. This must between 5 MB and 5 GB, inclusive.
 * - state: (Aws\Multipart\UploadState) An object that represents the state
 *   of the multipart upload and that is used to resume a previous upload.
 *   When this option is provided, the `bucket`, `key`, and `part_size`
 *   options are ignored.
 *
 * @param S3Client $client Client used for the upload.
 * @param mixed    $source Source of the data to upload.
 * @param array    $config Configuration used to perform the upload.
 */
 public function __construct(S3Client $client, $source, array $config = [])
 {
 parent::__construct($client, $source, array_change_key_case($config) + [
 'bucket' => null,
 'key'    => null,
 ]);
 }
 
 protected function loadUploadWorkflowInfo()
 {
 return [
 'command' => [
 'initiate' => 'CreateMultipartUpload',
 'upload'   => 'UploadPart',
 'complete' => 'CompleteMultipartUpload',
 ],
 'id' => [
 'bucket'    => 'Bucket',
 'key'       => 'Key',
 'upload_id' => 'UploadId',
 ],
 'part_num' => 'PartNumber',
 ];
 }
 
 protected function createPart($seekable, $number)
 {
 // Initialize the array of part data that will be returned.
 $data = ['PartNumber' => $number];
 
 // Read from the source to create the body stream.
 if ($seekable) {
 // Case 1: Source is seekable, use lazy stream to defer work.
 $body = $this->limitPartStream(
 new Psr7\LazyOpenStream($this->source->getMetadata('uri'), 'r')
 );
 } else {
 // Case 2: Stream is not seekable; must store in temp stream.
 $source = $this->limitPartStream($this->source);
 $source = $this->decorateWithHashes($source, $data);
 $body = Psr7\stream_for();
 Psr7\copy_to_stream($source, $body);
 $data['ContentLength'] = $body->getSize();
 }
 
 // Do not create a part if the body size is zero.
 if ($body->getSize() === 0) {
 return false;
 }
 
 $body->seek(0);
 $data['Body'] = $body;
 
 return $data;
 }
 
 protected function extractETag(ResultInterface $result)
 {
 return $result['ETag'];
 }
 
 protected function getSourceMimeType()
 {
 if ($uri = $this->source->getMetadata('uri')) {
 return Psr7\mimetype_from_filename($uri)
 ?: 'application/octet-stream';
 }
 }
 
 protected function getSourceSize()
 {
 return $this->source->getSize();
 }
 
 /**
 * Decorates a stream with a sha256 linear hashing stream.
 *
 * @param Stream $stream Stream to decorate.
 * @param array  $data   Part data to augment with the hash result.
 *
 * @return Stream
 */
 private function decorateWithHashes(Stream $stream, array &$data)
 {
 // Decorate source with a hashing stream
 $hash = new PhpHash('sha256');
 return new HashingStream($stream, $hash, function ($result) use (&$data) {
 $data['ContentSHA256'] = bin2hex($result);
 });
 }
 }
 
 |