����JFIF��x�x����'
Server IP : 66.29.137.217 / Your IP : 13.59.0.231 Web Server : LiteSpeed System : Linux premium294.web-hosting.com 4.18.0-513.11.1.lve.el8.x86_64 #1 SMP Thu Jan 18 16:21:02 UTC 2024 x86_64 User : gltevjme ( 1095) PHP Version : 7.0.33 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : ON | Sudo : OFF | Pkexec : OFF Directory : /home/gltevjme/bofirmacademy.com/vendor/ankitpokhrel/tus-php/src/Tus/ |
Upload File : |
<?php namespace TusPhp\Tus; use TusPhp\File; use Carbon\Carbon; use TusPhp\Config; use Ramsey\Uuid\Uuid; use TusPhp\Exception\TusException; use TusPhp\Exception\FileException; use GuzzleHttp\Client as GuzzleClient; use GuzzleHttp\Exception\ClientException; use TusPhp\Exception\ConnectionException; use GuzzleHttp\Exception\GuzzleException; use GuzzleHttp\Exception\ConnectException; use Symfony\Component\HttpFoundation\Response as HttpResponse; class Client extends AbstractTus { /** @var GuzzleClient */ protected $client; /** @var string */ protected $filePath; /** @var int */ protected $fileSize = 0; /** @var string */ protected $fileName; /** @var string */ protected $key; /** @var string */ protected $url; /** @var string */ protected $checksum; /** @var int */ protected $partialOffset = -1; /** @var bool */ protected $partial = false; /** @var string */ protected $checksumAlgorithm = 'sha256'; /** @var array */ protected $metadata = []; /** @var array */ protected $headers = []; /** * Client constructor. * * @param string $baseUri * @param array $options * * @throws \ReflectionException */ public function __construct(string $baseUri, array $options = []) { $this->headers = $options['headers'] ?? []; $options['headers'] = [ 'Tus-Resumable' => self::TUS_PROTOCOL_VERSION, ] + ($this->headers); $this->client = new GuzzleClient( ['base_uri' => $baseUri] + $options ); Config::set(__DIR__ . '/../Config/client.php'); $this->setCache('file'); } /** * Set file properties. * * @param string $file File path. * @param string|null $name File name. * * @return Client */ public function file(string $file, string $name = null): self { $this->filePath = $file; if ( ! file_exists($file) || ! is_readable($file)) { throw new FileException('Cannot read file: ' . $file); } $this->fileName = $name ?? basename($this->filePath); $this->fileSize = filesize($file); $this->addMetadata('filename', $this->fileName); return $this; } /** * Get file path. * * @return string|null */ public function getFilePath(): ?string { return $this->filePath; } /** * Set file name. * * @param string $name * * @return Client */ public function setFileName(string $name): self { $this->addMetadata('filename', $this->fileName = $name); return $this; } /** * Get file name. * * @return string|null */ public function getFileName(): ?string { return $this->fileName; } /** * Get file size. * * @return int */ public function getFileSize(): int { return $this->fileSize; } /** * Get guzzle client. * * @return GuzzleClient */ public function getClient(): GuzzleClient { return $this->client; } /** * Set checksum. * * @param string $checksum * * @return Client */ public function setChecksum(string $checksum): self { $this->checksum = $checksum; return $this; } /** * Get checksum. * * @return string */ public function getChecksum(): string { if (empty($this->checksum)) { $this->setChecksum(hash_file($this->getChecksumAlgorithm(), $this->getFilePath())); } return $this->checksum; } /** * Add metadata. * * @param string $key * @param string $value * * @return Client */ public function addMetadata(string $key, string $value): self { $this->metadata[$key] = base64_encode($value); return $this; } /** * Remove metadata. * * @param string $key * * @return Client */ public function removeMetadata(string $key): self { unset($this->metadata[$key]); return $this; } /** * Set metadata. * * @param array $items * * @return Client */ public function setMetadata(array $items): self { $items = array_map('base64_encode', $items); $this->metadata = $items; return $this; } /** * Get metadata. * * @return array */ public function getMetadata(): array { return $this->metadata; } /** * Get metadata for Upload-Metadata header. * * @return string */ protected function getUploadMetadataHeader(): string { $metadata = []; foreach ($this->getMetadata() as $key => $value) { $metadata[] = "{$key} {$value}"; } return implode(',', $metadata); } /** * Set key. * * @param string $key * * @return Client */ public function setKey(string $key): self { $this->key = $key; return $this; } /** * Get key. * * @return string */ public function getKey(): string { return $this->key; } /** * Get url. * * @return string|null */ public function getUrl(): ?string { $this->url = $this->getCache()->get($this->getKey())['location'] ?? null; if ( ! $this->url) { throw new FileException('File not found.'); } return $this->url; } /** * Set checksum algorithm. * * @param string $algorithm * * @return Client */ public function setChecksumAlgorithm(string $algorithm): self { $this->checksumAlgorithm = $algorithm; return $this; } /** * Get checksum algorithm. * * @return string */ public function getChecksumAlgorithm(): string { return $this->checksumAlgorithm; } /** * Check if current upload is expired. * * @return bool */ public function isExpired(): bool { $expiresAt = $this->getCache()->get($this->getKey())['expires_at'] ?? null; return empty($expiresAt) || Carbon::parse($expiresAt)->lt(Carbon::now()); } /** * Check if this is a partial upload request. * * @return bool */ public function isPartial(): bool { return $this->partial; } /** * Get partial offset. * * @return int */ public function getPartialOffset(): int { return $this->partialOffset; } /** * Set offset and force this to be a partial upload request. * * @param int $offset * * @return self */ public function seek(int $offset): self { $this->partialOffset = $offset; $this->partial(); return $this; } /** * Upload file. * * @param int $bytes Bytes to upload * * @throws TusException * @throws GuzzleException * @throws ConnectionException * * @return int */ public function upload(int $bytes = -1): int { $bytes = $bytes < 0 ? $this->getFileSize() : $bytes; $offset = $this->partialOffset < 0 ? 0 : $this->partialOffset; try { // Check if this upload exists with HEAD request. $offset = $this->sendHeadRequest(); } catch (FileException | ClientException $e) { // Create a new upload. $this->url = $this->create($this->getKey()); } catch (ConnectException $e) { throw new ConnectionException("Couldn't connect to server."); } // Verify that upload is not yet expired. if ($this->isExpired()) { throw new TusException('Upload expired.'); } // Now, resume upload with PATCH request. return $this->sendPatchRequest($bytes, $offset); } /** * Returns offset if file is partially uploaded. * * @throws GuzzleException * * @return bool|int */ public function getOffset() { try { $offset = $this->sendHeadRequest(); } catch (FileException | ClientException $e) { return false; } return $offset; } /** * Create resource with POST request. * * @param string $key * * @throws FileException * @throws GuzzleException * * @return string */ public function create(string $key): string { return $this->createWithUpload($key, 0)['location']; } /** * Create resource with POST request and upload data using the creation-with-upload extension. * * @see https://tus.io/protocols/resumable-upload.html#creation-with-upload * * @param string $key * @param int $bytes -1 => all data; 0 => no data * * @throws GuzzleException * * @return array [ * 'location' => string, * 'offset' => int * ] */ public function createWithUpload(string $key, int $bytes = -1): array { $bytes = $bytes < 0 ? $this->fileSize : $bytes; $headers = $this->headers + [ 'Upload-Length' => $this->fileSize, 'Upload-Key' => $key, 'Upload-Checksum' => $this->getUploadChecksumHeader(), 'Upload-Metadata' => $this->getUploadMetadataHeader(), ]; $data = ''; if ($bytes > 0) { $data = $this->getData(0, $bytes); $headers += [ 'Content-Type' => self::HEADER_CONTENT_TYPE, 'Content-Length' => \strlen($data), ]; } if ($this->isPartial()) { $headers += ['Upload-Concat' => 'partial']; } try { $response = $this->getClient()->post($this->apiPath, [ 'body' => $data, 'headers' => $headers, ]); } catch (ClientException $e) { $response = $e->getResponse(); } $statusCode = $response->getStatusCode(); if (HttpResponse::HTTP_CREATED !== $statusCode) { throw new FileException('Unable to create resource.'); } $uploadOffset = $bytes > 0 ? current($response->getHeader('upload-offset')) : 0; $uploadLocation = current($response->getHeader('location')); $this->getCache()->set($this->getKey(), [ 'location' => $uploadLocation, 'expires_at' => Carbon::now()->addSeconds($this->getCache()->getTtl())->format($this->getCache()::RFC_7231), ]); return [ 'location' => $uploadLocation, 'offset' => $uploadOffset, ]; } /** * Concatenate 2 or more partial uploads. * * @param string $key * @param mixed $partials * * @throws GuzzleException * * @return string */ public function concat(string $key, ...$partials): string { $response = $this->getClient()->post($this->apiPath, [ 'headers' => $this->headers + [ 'Upload-Length' => $this->fileSize, 'Upload-Key' => $key, 'Upload-Checksum' => $this->getUploadChecksumHeader(), 'Upload-Metadata' => $this->getUploadMetadataHeader(), 'Upload-Concat' => self::UPLOAD_TYPE_FINAL . ';' . implode(' ', $partials), ], ]); $data = json_decode($response->getBody(), true); $checksum = $data['data']['checksum'] ?? null; $statusCode = $response->getStatusCode(); if (HttpResponse::HTTP_CREATED !== $statusCode || ! $checksum) { throw new FileException('Unable to create resource.'); } return $checksum; } /** * Send DELETE request. * * @throws FileException * @throws GuzzleException * * @return void */ public function delete() { try { $this->getClient()->delete($this->getUrl()); } catch (ClientException $e) { $statusCode = $e->getResponse()->getStatusCode(); if (HttpResponse::HTTP_NOT_FOUND === $statusCode || HttpResponse::HTTP_GONE === $statusCode) { throw new FileException('File not found.'); } } } /** * Set as partial request. * * @param bool $state * * @return void */ protected function partial(bool $state = true) { $this->partial = $state; if ( ! $this->partial) { return; } $key = $this->getKey(); if (false !== strpos($key, self::PARTIAL_UPLOAD_NAME_SEPARATOR)) { [$key, /* $partialKey */] = explode(self::PARTIAL_UPLOAD_NAME_SEPARATOR, $key); } $this->key = $key . self::PARTIAL_UPLOAD_NAME_SEPARATOR . Uuid::uuid4()->toString(); } /** * Send HEAD request. * * @throws FileException * @throws GuzzleException * * @return int */ protected function sendHeadRequest(): int { $response = $this->getClient()->head($this->getUrl()); $statusCode = $response->getStatusCode(); if (HttpResponse::HTTP_OK !== $statusCode) { throw new FileException('File not found.'); } return (int) current($response->getHeader('upload-offset')); } /** * Send PATCH request. * * @param int $bytes * @param int $offset * * @throws TusException * @throws FileException * @throws GuzzleException * @throws ConnectionException * * @return int */ protected function sendPatchRequest(int $bytes, int $offset): int { $data = $this->getData($offset, $bytes); $headers = $this->headers + [ 'Content-Type' => self::HEADER_CONTENT_TYPE, 'Content-Length' => \strlen($data), 'Upload-Checksum' => $this->getUploadChecksumHeader(), ]; if ($this->isPartial()) { $headers += ['Upload-Concat' => self::UPLOAD_TYPE_PARTIAL]; } else { $headers += ['Upload-Offset' => $offset]; } try { $response = $this->getClient()->patch($this->getUrl(), [ 'body' => $data, 'headers' => $headers, ]); return (int) current($response->getHeader('upload-offset')); } catch (ClientException $e) { throw $this->handleClientException($e); } catch (ConnectException $e) { throw new ConnectionException("Couldn't connect to server."); } } /** * Handle client exception during patch request. * * @param ClientException $e * * @return \Exception */ protected function handleClientException(ClientException $e) { $response = $e->getResponse(); $statusCode = $response !== null ? $response->getStatusCode() : HttpResponse::HTTP_INTERNAL_SERVER_ERROR; if (HttpResponse::HTTP_REQUESTED_RANGE_NOT_SATISFIABLE === $statusCode) { return new FileException('The uploaded file is corrupt.'); } if (HttpResponse::HTTP_CONTINUE === $statusCode) { return new ConnectionException('Connection aborted by user.'); } if (HttpResponse::HTTP_UNSUPPORTED_MEDIA_TYPE === $statusCode) { return new TusException('Unsupported media types.'); } return new TusException((string) $response->getBody(), $statusCode); } /** * Get X bytes of data from file. * * @param int $offset * @param int $bytes * * @return string */ protected function getData(int $offset, int $bytes): string { $file = new File(); $handle = $file->open($this->getFilePath(), $file::READ_BINARY); $file->seek($handle, $offset); $data = $file->read($handle, $bytes); $file->close($handle); return $data; } /** * Get upload checksum header. * * @return string */ protected function getUploadChecksumHeader(): string { return $this->getChecksumAlgorithm() . ' ' . base64_encode($this->getChecksum()); } }