123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175 |
- <?php
- namespace 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;
- use Aws\S3\Exception\S3MultipartUploadException;
- /**
- * 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, or an S3 access point ARN.
- * - 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.
- * - params: (array) An array of key/value parameters that will be applied
- * to each of the sub-commands run by the uploader as a base.
- * Auto-calculated options will override these parameters. If you need
- * more granularity over parameters to each sub-command, use the before_*
- * options detailed above to update the commands directly.
- * - 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.
- * - prepare_data_source: (callable) Callback to invoke before starting the
- * multipart upload workflow. The callback should have a function
- * signature like `function () {...}`.
- * - 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 S3ClientInterface $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(
- S3ClientInterface $client,
- $source,
- array $config = []
- ) {
- parent::__construct($client, $source, array_change_key_case($config) + [
- 'bucket' => null,
- 'key' => null,
- 'exception_class' => S3MultipartUploadException::class,
- ]);
- }
- 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 = [];
- // Apply custom params to UploadPart data
- $config = $this->getConfig();
- $params = isset($config['params']) ? $config['params'] : [];
- foreach ($params as $k => $v) {
- $data[$k] = $v;
- }
- $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\Utils::streamFor();
- Psr7\Utils::copyToStream($source, $body);
- }
- $contentLength = $body->getSize();
- // Do not create a part if the body size is zero.
- if ($contentLength === 0) {
- return false;
- }
- $body->seek(0);
- $data['Body'] = $body;
- if (isset($config['add_content_md5'])
- && $config['add_content_md5'] === true
- ) {
- $data['AddContentMD5'] = true;
- }
- $data['ContentLength'] = $contentLength;
- return $data;
- }
- protected function extractETag(ResultInterface $result)
- {
- return $result['ETag'];
- }
- protected function getSourceMimeType()
- {
- if ($uri = $this->source->getMetadata('uri')) {
- return Psr7\MimeType::fromFilename($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);
- });
- }
- }
|