Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ISSUE-7: S3ObjectSeeker #10

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 141 additions & 0 deletions src/Seeker/S3ObjectSeeker.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
<?php

namespace Mingulay\Seeker;

use Exception;
use InvalidArgumentException;
use Mingulay\SeekerInterface;

class S3ObjectSeeker implements SeekerInterface {

/**
* The valid s3:// wrapper path to an S3 Object.
*
* @var string
*/
protected $path;

/**
* The file pointer.
*
* @var false|resource
*/
protected $stream;

/**
* The size of the file.
*
* @var int
*/
protected $size;


/**
* Create a new S3ObjectSeeker object.
*
* @param string $path The streamwrapper prefixed path to an S3 Object.
*
* @throws InvalidArgumentException if the path is not accessible.
*/
public function __construct(string $path) {
$this->path = $path;
try {
$this->size = filesize($path);
if ($this->size === FALSE) {
throw new InvalidArgumentException(
"The provided S3 Object could not be opened"
);
}
} catch (Exception $e) {
throw new InvalidArgumentException(
"The provided S3 Object could not be opened: " . $e->getMessage()
);
}
}

/**
* Destructor function to ensure the file descriptor is closed.
*/
public function __destruct() {
if ($this->stream) {
digitaldogsbody marked this conversation as resolved.
Show resolved Hide resolved
fclose($this->stream);
}
}

/**
* @inheritDoc
*/
public function retrieveStart(int $length, int $offset = 0): ?string {
// Bail out if the parameters are invalid
if ($length <= 0 || $offset < 0 || ($offset + $length) > $this->size) {
return NULL;
}

try {
$context = stream_context_create(
[
's3' => [
'Range' => "bytes=" . $offset . "-" . ($offset + $length - 1),
'seekable' => FALSE,
],
]
);

$this->stream = fopen($this->path, "r", FALSE, $context);
$data = '';
while (!feof( $this->stream)) {
$data .= fread($this->stream, 8192);
}
if (!$data or strlen($data)!= $length) {
return NULL;
}
return $data;
} catch (Exception $e) {
user_error("Caught exception: " . $e->getMessage(), E_USER_WARNING);
return NULL;
}
}

/**
* @inheritDoc
*/
public function retrieveEnd(int $length, int $offset = 0): ?string {
if ($length <= 0 || ($offset + $length) > $this->size) {
return NULL;
}
// Make sure we can handle both positive and negative expressions of the offset, just in case.
$offset = abs($offset);

// Make sure we are not trying to seek past the start of the file.
if ($offset > $this->size) {
$offset = $this->size;
}

try {
$to = ($this->size - $offset - 1);
$from = $to - $length + 1;
$context = stream_context_create(
[
's3' => [
'Range' => "bytes=" . $from
. "-" . $to,
'seekable' => FALSE,
],
]
);
$data = '';
$this->stream = fopen($this->path, "r", FALSE, $context);
while (!feof( $this->stream)) {
$data .= fread($this->stream, 8192);
}
// Be sure to close the stream resource when you're done with it
if (!$data || strlen($data)!= $length) {
return NULL;
}
return $data;
} catch (Exception $e) {
user_error("Caught exception: " . $e->getMessage(), E_USER_WARNING);
return NULL;
}
}
}