uploadProgressCallback = $options['uploadProgressCallback']; } elseif (isset($options['uploadProgressCallback'])) { throw new \InvalidArgumentException('$options.uploadProgressCallback must be a callable.'); } } /** * Gets the resume URI. * * @return string */ public function getResumeUri() { if (!$this->resumeUri) { return $this->createResumeUri(); } return $this->resumeUri; } /** * Resumes a download using the provided URI. * * @param string $resumeUri * @return array * @throws GoogleException */ public function resume($resumeUri) { if (!$this->data->isSeekable()) { throw new \DeliciousBrains\WP_Offload_Media\Gcp\Google\Cloud\Core\Exception\GoogleException('Cannot resume upload on a stream which cannot be seeked.'); } $this->resumeUri = $resumeUri; $response = $this->getStatusResponse(); if ($response->getBody()->getSize() > 0) { return $this->decodeResponse($response); } $this->rangeStart = $this->getRangeStart($response->getHeaderLine('Range')); return $this->upload(); } /** * Triggers the upload process. * * @return array * @throws GoogleException */ public function upload() { $rangeStart = $this->rangeStart; $response = null; $resumeUri = $this->getResumeUri(); $size = $this->data->getSize() ?: '*'; do { $data = new \DeliciousBrains\WP_Offload_Media\Gcp\GuzzleHttp\Psr7\LimitStream($this->data, $this->chunkSize ?: -1, $rangeStart); $currStreamLimitSize = $data->getSize(); $rangeEnd = $rangeStart + ($currStreamLimitSize - 1); $headers = $this->headers + ['Content-Length' => $currStreamLimitSize, 'Content-Type' => $this->contentType, 'Content-Range' => "bytes {$rangeStart}-{$rangeEnd}/{$size}"]; $request = new \DeliciousBrains\WP_Offload_Media\Gcp\GuzzleHttp\Psr7\Request('PUT', $resumeUri, $headers, $data); try { $response = $this->requestWrapper->send($request, $this->requestOptions); } catch (GoogleException $ex) { throw new \DeliciousBrains\WP_Offload_Media\Gcp\Google\Cloud\Core\Exception\GoogleException("Upload failed. Please use this URI to resume your upload: {$this->resumeUri}", $ex->getCode()); } if (is_callable($this->uploadProgressCallback)) { call_user_func($this->uploadProgressCallback, $currStreamLimitSize); } $rangeStart = $this->getRangeStart($response->getHeaderLine('Range')); } while ($response->getStatusCode() === 308); return $this->decodeResponse($response); } /** * Fetch and decode the response body * * @param ResponseInterface $response * @return array */ protected function decodeResponse(\DeliciousBrains\WP_Offload_Media\Gcp\Psr\Http\Message\ResponseInterface $response) { return $this->jsonDecode($response->getBody(), true); } /** * Creates the resume URI. * * @return string */ protected function createResumeUri() { $headers = $this->headers + ['X-Upload-Content-Type' => $this->contentType, 'X-Upload-Content-Length' => $this->data->getSize(), 'Content-Type' => 'application/json']; $body = $this->jsonEncode($this->metadata); $request = new \DeliciousBrains\WP_Offload_Media\Gcp\GuzzleHttp\Psr7\Request('POST', $this->uri, $headers, $body); $response = $this->requestWrapper->send($request, $this->requestOptions); return $this->resumeUri = $response->getHeaderLine('Location'); } /** * Gets the status of the upload. * * @return ResponseInterface */ protected function getStatusResponse() { $request = new \DeliciousBrains\WP_Offload_Media\Gcp\GuzzleHttp\Psr7\Request('PUT', $this->resumeUri, ['Content-Range' => 'bytes */*']); return $this->requestWrapper->send($request, $this->requestOptions); } /** * Gets the starting range for the upload. * * @param string $rangeHeader * @return int */ protected function getRangeStart($rangeHeader) { if (!$rangeHeader) { return null; } return (int) explode('-', $rangeHeader)[1] + 1; } }