| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175 | <?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;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);        });    }}
 |