You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
yanzong/vendor/qcloud/cos-sdk-v5/src/MultipartUpload.php

160 lines
6.1 KiB

10 months ago
<?php
namespace Qcloud\Cos;
use GuzzleHttp\Pool;
class MultipartUpload {
const MIN_PART_SIZE = 1048576;
const MAX_PART_SIZE = 5368709120;
const DEFAULT_PART_SIZE = 5242880;
const MAX_PARTS = 10000;
private $client;
private $options;
private $partSize;
private $parts;
private $body;
private $progress;
private $totalSize;
private $uploadedSize;
public function __construct($client, $body, $options = array()) {
$minPartSize = $options['PartSize'];
unset($options['PartSize']);
$this->body = $body;
$this->client = $client;
$this->options = $options;
$this->partSize = $this->calculatePartSize($minPartSize);
$this->concurrency = isset($options['Concurrency']) ? $options['Concurrency'] : 10;
$this->progress = isset($options['Progress']) ? $options['Progress'] : function($totalSize, $uploadedSize) {};
$this->parts = [];
$this->partNumberList = [];
$this->uploadedSize = 0;
$this->totalSize = $this->body->getSize();
$this->needMd5 = isset($options['ContentMD5']) ? $options['ContentMD5'] : true;
$this->retry = isset($options['Retry']) ? $options['Retry'] : 3;
}
public function performUploading() {
$uploadId= $this->initiateMultipartUpload();
$this->uploadParts($uploadId);
foreach ( $this->parts as $key => $row ){
$num1[$key] = $row ['PartNumber'];
$num2[$key] = $row ['ETag'];
}
array_multisort($num1, SORT_ASC, $num2, SORT_ASC, $this->parts);
return $this->client->completeMultipartUpload(array(
'Bucket' => $this->options['Bucket'],
'Key' => $this->options['Key'],
'UploadId' => $uploadId,
'Parts' => $this->parts)
);
}
public function uploadParts($uploadId) {
$uploadRequests = function ($uploadId) {
$partNumber = 1;
$index = 1;
$offset = 0;
$partSize = 0;
for ( ; ; $partNumber ++) {
if ($this->body->eof()) {
break;
}
$body = $this->body->read($this->partSize);
$partSize = $this->partSize;
if ($offset + $this->partSize >= $this->totalSize) {
$partSize = $this->totalSize - $offset;
}
$offset += $partSize;
if (empty($body)) {
break;
}
if (isset($this->parts[$partNumber])) {
continue;
}
$this->partNumberList[$index]['PartNumber'] = $partNumber;
$this->partNumberList[$index]['PartSize'] = $partSize;
$params = array(
'Bucket' => $this->options['Bucket'],
'Key' => $this->options['Key'],
'UploadId' => $uploadId,
'PartNumber' => $partNumber,
'Body' => $body,
'ContentMD5' => $this->needMd5
);
if ($this->needMd5 == false) {
unset($params["ContentMD5"]);
}
if (!isset($this->parts[$partNumber])) {
$command = $this->client->getCommand('uploadPart', $params);
$request = $this->client->commandToRequestTransformer($command);
$index ++;
yield $request;
}
}
};
$pool = new Pool($this->client->httpClient, $uploadRequests($uploadId), [
'concurrency' => $this->concurrency,
'fulfilled' => function ($response, $index) {
$index = $index + 1;
$partNumber = $this->partNumberList[$index]['PartNumber'];
$partSize = $this->partNumberList[$index]['PartSize'];
$etag = $response->getHeaders()["ETag"][0];
$part = array('PartNumber' => $partNumber, 'ETag' => $etag);
$this->parts[$partNumber] = $part;
$this->uploadedSize += $partSize;
call_user_func_array($this->progress, [$this->totalSize, $this->uploadedSize]);
},
'rejected' => function ($reason, $index) {
printf("part [%d] upload failed, reason: %s\n", $index, $reason);
throw($reason);
}
]);
$promise = $pool->promise();
$promise->wait();
}
public function resumeUploading() {
$uploadId = $this->options['UploadId'];
$rt = $this->client->ListParts(
array('UploadId' => $uploadId,
'Bucket'=>$this->options['Bucket'],
'Key'=>$this->options['Key']));
$parts = array();
if (count($rt['Parts']) > 0) {
foreach ($rt['Parts'] as $part) {
$this->parts[$part['PartNumber']] = array('PartNumber' => $part['PartNumber'], 'ETag' => $part['ETag']);
}
}
$this->uploadParts($uploadId);
foreach ( $this->parts as $key => $row ){
$num1[$key] = $row ['PartNumber'];
$num2[$key] = $row ['ETag'];
}
array_multisort($num1, SORT_ASC, $num2, SORT_ASC, $this->parts);
return $this->client->completeMultipartUpload(array(
'Bucket' => $this->options['Bucket'],
'Key' => $this->options['Key'],
'UploadId' => $uploadId,
'Parts' => $this->parts)
);
}
private function calculatePartSize($minPartSize)
{
$partSize = intval(ceil(($this->body->getSize() / self::MAX_PARTS)));
$partSize = max($minPartSize, $partSize);
$partSize = min($partSize, self::MAX_PART_SIZE);
$partSize = max($partSize, self::MIN_PART_SIZE);
return $partSize;
}
private function initiateMultipartUpload() {
$result = $this->client->createMultipartUpload($this->options);
return $result['UploadId'];
}
}