- Модуль: disk
- Путь к файлу: ~/bitrix/modules/disk/lib/document/upload/resumableupload.php
- Класс: BitrixDiskDocumentUploadResumableUpload
- Вызов: ResumableUpload::upload
public function upload()
{
if(!$this->checkRequiredInputParams($this->fileData->toArray(), array(
'src', 'mimeType',
)))
{
return false;
}
if(!$this->fileData->getSize())
{
$this->fileData->setSize(filesize($this->fileData->getSrc()));
}
$chunkSize = self::CHUNK_SIZE;
$locationForUpload = $this->getLocationForResumableUpload();
if(!$locationForUpload)
{
return false;
}
/** @var HttpClient $http */
$http = null;
$lastResponseCode = false;
$fileMetadata = null;
$lastRange = false;
$transactionCounter = 0;
$doExponentialBackoff = false;
$exponentialBackoffCounter = 0;
$response = array();
while ($lastResponseCode === false || $lastResponseCode == 308 || $lastResponseCode == 202)
{
$transactionCounter++;
if ($doExponentialBackoff)
{
$sleepFor = pow(2, $exponentialBackoffCounter);
sleep($sleepFor);
usleep(rand(0, 1000));
$exponentialBackoffCounter++;
if ($exponentialBackoffCounter > 5)
{
$this->lastStatus = $http? $http->getStatus() : 0;
$this->errorCollection[] = new Error(
"Could not upload part (Exponential back off) ({$this->lastStatus})",
self::ERROR_HTTP_RESUMABLE_UPLOAD
);
return false;
}
}
// determining what range is next
$rangeStart = $this->getNextStartRange($http);
$rangeEnd = min($chunkSize, $this->fileData->getSize() - 1);
if ($rangeStart > 0)
{
$rangeEnd = min($rangeStart + $chunkSize, $this->fileData->getSize() - 1);
}
$http = new HttpClient(array(
'socketTimeout' => 10,
'streamTimeout' => 30,
'version' => HttpClient::HTTP_1_1,
));
$this->setBearer($http);
$http->setHeader('Content-Length', (string)($rangeEnd - $rangeStart + 1));
$http->setHeader('Content-Type', $this->fileData->getMimeType());
$http->setHeader('Content-Range', "bytes {$rangeStart}-{$rangeEnd}/{$this->fileData->getSize()}");
$toSendContent = file_get_contents($this->fileData->getSrc(), false, null, $rangeStart, ($rangeEnd - $rangeStart + 1));
if($http->query('PUT', $locationForUpload, $toSendContent))
{
$response['headers']['range'] = $http->getHeaders()->get('Range');
}
$doExponentialBackoff = false;
if ($http->getStatus())
{
// checking for expired credentials
if ($http->getStatus() == 401)
{
$this->documentHandler->queryAccessToken();
$lastResponseCode = false;
}
else if ($http->getStatus() == 308 || $http->getStatus() == 202)
{
// todo: verify x-range-md5 header to be sure
$lastResponseCode = $http->getStatus();
$exponentialBackoffCounter = 0;
}
else if ($http->getStatus() == 503)
{ // Google's letting us know we should retry
$doExponentialBackoff = true;
$lastResponseCode = false;
}
else
{
if (in_array($http->getStatus(), array(200, 201)))
{ // we are done!
$lastResponseCode = $http->getStatus();
}
else
{
$this->lastStatus = $http->getStatus();
$this->errorCollection[] = new Error(
"Could not upload part ({$this->lastStatus})",
self::ERROR_HTTP_RESUMABLE_UPLOAD
);
return false;
}
}
}
else
{
$doExponentialBackoff = true;
$lastResponseCode = false;
}
}
if ($lastResponseCode != 200 && $lastResponseCode != 201)
{
$this->lastStatus = $http->getStatus();
$this->errorCollection[] = new Error(
"Could not upload final part ({$this->lastStatus})",
self::ERROR_HTTP_RESUMABLE_UPLOAD
);
return false;
}
$this->lastResponse = null;
if(isset($http))
{
$this->lastResponse = Json::decode($http->getResult());
}
if($this->lastResponse === null)
{
$this->errorCollection[] = new Error(
'Could not decode response as json', self::ERROR_BAD_JSON
);
return false;
}
$this->fillFileDataByResponse($this->fileData, $this->lastResponse);
return true;
}