/** * @param FileData $fileData * @param string $lastStatus * @param array $fileMetadata * @return FileData|null */ protected function createByResumableUpload(FileData $fileData, &$lastStatus, &$fileMetadata) { if (!$this->checkRequiredInputParams($fileData->toArray(), array('src', 'mimeType'))) { return null; } $accessToken = $this->getAccessToken(); if (!$fileData->getSize()) { $fileData->setSize(filesize($fileData->getSrc())); } $chunkSize = 40 * 256 * 1024; // Chunk size restriction: All chunks must be a multiple of 256 KB (256 x 1024 bytes) in size except for the final chunk that completes the upload $locationForUpload = $this->getLocationForResumableUpload($fileData); if (!$locationForUpload) { return null; } $lastResponseCode = false; $fileMetadata = null; $lastRange = false; $transactionCounter = 0; $doExponentialBackoff = false; $exponentialBackoffCounter = 0; $response = array(); while ($lastResponseCode === false || $lastResponseCode == '308') { $transactionCounter++; if ($doExponentialBackoff) { $sleepFor = pow(2, $exponentialBackoffCounter); sleep($sleepFor); usleep(rand(0, 1000)); $exponentialBackoffCounter++; if ($exponentialBackoffCounter > 5) { $lastStatus = $response['code']; $this->errorCollection->add(array(new Error("Could not upload part (Exponential back off) ({$lastStatus})", self::ERROR_HTTP_RESUMABLE_UPLOAD))); return null; } } // determining what range is next $rangeStart = 0; $rangeEnd = min($chunkSize, $fileData->getSize() - 1); if ($lastRange !== false) { $lastRange = explode('-', $lastRange); $rangeStart = (int) $lastRange[1] + 1; $rangeEnd = min($rangeStart + $chunkSize, $fileData->getSize() - 1); } $http = new HttpClient(array('socketTimeout' => 10, 'streamTimeout' => 30, 'version' => HttpClient::HTTP_1_1)); $http->setHeader('Authorization', "Bearer {$accessToken}"); $http->setHeader('Content-Length', (string) ($rangeEnd - $rangeStart + 1)); $http->setHeader('Content-Type', $fileData->getMimeType()); $http->setHeader('Content-Range', "bytes {$rangeStart}-{$rangeEnd}/{$fileData->getSize()}"); $toSendContent = file_get_contents($fileData->getSrc(), false, null, $rangeStart, $rangeEnd - $rangeStart + 1); if ($http->query('PUT', $locationForUpload, $toSendContent)) { $response['code'] = $http->getStatus(); $response['headers']['range'] = $http->getHeaders()->get('Range'); } $doExponentialBackoff = false; if (isset($response['code'])) { // checking for expired credentials if ($response['code'] == "401") { // todo: make sure that we also got an invalid credential response //$access_token = get_access_token(true); $lastResponseCode = false; } else { if ($response['code'] == "308") { $lastResponseCode = $response['code']; $lastRange = $response['headers']['range']; // todo: verify x-range-md5 header to be sure $exponentialBackoffCounter = 0; } else { if ($response['code'] == "503") { // Google's letting us know we should retry $doExponentialBackoff = true; $lastResponseCode = false; } else { if ($response['code'] == "200") { // we are done! $lastResponseCode = $response['code']; } else { $lastStatus = $response['code']; $this->errorCollection->add(array(new Error("Could not upload part ({$lastStatus})", self::ERROR_HTTP_RESUMABLE_UPLOAD))); return null; } } } } } else { $doExponentialBackoff = true; $lastResponseCode = false; } } if ($lastResponseCode != "200") { $lastStatus = $response['code']; $this->errorCollection->add(array(new Error("Could not upload final part ({$lastStatus})", self::ERROR_HTTP_RESUMABLE_UPLOAD))); return null; } $fileMetadata = null; if (isset($http)) { $fileMetadata = Json::decode($http->getResult()); } if ($fileMetadata === null) { $this->errorCollection->add(array(new Error('Could not decode response as json', self::ERROR_BAD_JSON))); return null; } $fileData->setLinkInService($fileMetadata['alternateLink']); $fileData->setId($fileMetadata['id']); return $fileData; }
protected function processActionPublish() { $onlineSession = $this->getOnlineEditSessionForFile(); if ($onlineSession) { $forkSession = $onlineSession; if ($onlineSession->getOwnerId() != $this->getUser()->getId()) { $forkSession = $this->forkEditSessionForCurrentUser($onlineSession); } $this->sendJsonSuccessResponse(array('editSessionId' => $forkSession->getId(), 'id' => $onlineSession->getServiceFileId(), 'link' => $onlineSession->getServiceFileLink())); } $src = $this->getFileSrcToPublish(); if (!$src) { $this->errorCollection->add(array(new Error(Loc::getMessage('DISK_DOC_CONTROLLER_ERROR_COULD_NOT_GET_FILE'), self::ERROR_COULD_NOT_GET_FILE))); $this->sendJsonErrorResponse(); } $fileData = new FileData(); $fileData->setName($this->file->getName()); $fileData->setMimeType(TypeFile::getMimeTypeByFilename($this->file->getName())); $fileData->setSrc($src); if (!$fileData->getSize()) { $fileData->setSize(filesize($fileData->getSrc())); } if ($fileData->getSize() === 0) { $fileData = $this->documentHandler->createBlankFile($fileData); } else { $fileData = $this->documentHandler->createFile($fileData); } if (!$fileData) { if ($this->documentHandler->isRequiredAuthorization()) { $this->sendNeedAuth(); } $this->errorCollection->add($this->documentHandler->getErrors()); $this->sendJsonErrorResponse(); } //if somebody publish to google similar document $onlineSession = $this->getOnlineEditSessionForFile(); if ($onlineSession) { $this->documentHandler->deleteFile($fileData); $forkSession = $onlineSession; if ($onlineSession->getOwnerId() != $this->getUser()->getId()) { $forkSession = $this->forkEditSessionForCurrentUser($onlineSession); } $this->sendJsonSuccessResponse(array('editSessionId' => $forkSession->getId(), 'id' => $onlineSession->getServiceFileId(), 'link' => $onlineSession->getServiceFileLink())); } $session = $this->addFileEditSessionByCurrentUser($fileData); $this->sendJsonSuccessResponse(array('editSessionId' => $session->getId(), 'id' => $fileData->getId(), 'link' => $fileData->getLinkInService())); }