232 lines
7.5 KiB
PHP
232 lines
7.5 KiB
PHP
<?php
|
|
|
|
namespace App\AlbumSources;
|
|
|
|
use App\Helpers\MiscHelper;
|
|
use App\Photo;
|
|
use GuzzleHttp\Exception\GuzzleException;
|
|
use GuzzleHttp\Psr7\Stream;
|
|
use Illuminate\Support\Facades\Log;
|
|
use function GuzzleHttp\Psr7\stream_for;
|
|
|
|
class AmazonS3Source extends AlbumSourceBase implements IAlbumSource, IAnalysisQueueSource
|
|
{
|
|
/**
|
|
* Deletes an entire album's media contents.
|
|
* @return void
|
|
*/
|
|
public function deleteAlbumContents()
|
|
{
|
|
// Because all photos live in a single bucket, there is nothing "global" to delete for the entire album
|
|
// The delete routine will have already removed all photos
|
|
}
|
|
|
|
/**
|
|
* Deletes a photo to be analysed from the storage source
|
|
* @param string $queueToken Queue token holding the photo
|
|
* @param string $fileName Filename of the photo to download
|
|
* @return void
|
|
*/
|
|
public function deleteItemFromAnalysisQueue($queueToken, $fileName)
|
|
{
|
|
$fileToDelete = $this->getPathToAnalysisQueueItem($queueToken, $fileName);
|
|
|
|
try
|
|
{
|
|
$this->getClient()->deleteObject([
|
|
'Bucket' => $this->configuration->container_name,
|
|
'Key' => $fileToDelete
|
|
]);
|
|
}
|
|
catch (GuzzleException $ex)
|
|
{
|
|
// Don't worry if the file no longer exists
|
|
Log::warning('Failed deleting image from S3.', ['error' => $ex->getMessage(), 'path' => $fileToDelete]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Deletes a thumbnail file for a photo.
|
|
* @param Photo $photo Photo to delete the thumbnail from.
|
|
* @param string $thumbnail Thumbnail to delete (or null to delete the original.)
|
|
* @return void
|
|
*/
|
|
public function deleteThumbnail(Photo $photo, $thumbnail = null)
|
|
{
|
|
$photoPath = $this->getPathToPhoto($photo, $thumbnail);
|
|
|
|
try
|
|
{
|
|
$this->getClient()->deleteObject([
|
|
'Bucket' => $this->configuration->container_name,
|
|
'Key' => $this->getPathToPhoto($photo, $thumbnail)
|
|
]);
|
|
}
|
|
catch (GuzzleException $ex)
|
|
{
|
|
// Don't worry if the file no longer exists
|
|
Log::warning('Failed deleting image from S3.', ['error' => $ex->getMessage(), 'path' => $photoPath]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Downloads a photo to be analysed from the storage source to a temporary file
|
|
* @param string $queueToken Queue token holding the photo
|
|
* @param string $fileName Filename of the photo to download
|
|
* @return string Path to the photo that was downloaded
|
|
*/
|
|
public function fetchItemFromAnalysisQueue($queueToken, $fileName)
|
|
{
|
|
$tempFile = tempnam(sys_get_temp_dir(), 'BlueTwilight_');
|
|
|
|
$this->getClient()->getObject([
|
|
'Bucket' => $this->configuration->container_name,
|
|
'Key' => $this->getPathToAnalysisQueueItem($queueToken, $fileName),
|
|
'SaveAs' => $tempFile
|
|
]);
|
|
|
|
return $tempFile;
|
|
}
|
|
|
|
/**
|
|
* Fetches the contents of a thumbnail for a photo.
|
|
* @param Photo $photo Photo to fetch the thumbnail for.
|
|
* @param string $thumbnail Thumbnail to fetch (or null to fetch the original.)
|
|
* @return Stream
|
|
*/
|
|
public function fetchPhotoContent(Photo $photo, $thumbnail = null)
|
|
{
|
|
$tempFile = tempnam(sys_get_temp_dir(), 'BlueTwilight_');
|
|
|
|
try
|
|
{
|
|
$this->getClient()->getObject([
|
|
'Bucket' => $this->configuration->container_name,
|
|
'Key' => $this->getPathToPhoto($photo, $thumbnail),
|
|
'SaveAs' => $tempFile
|
|
]);
|
|
|
|
return stream_for(fopen($tempFile, 'r+'));
|
|
}
|
|
finally
|
|
{
|
|
@unlink($tempFile);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets the name of this album source.
|
|
* @return string
|
|
*/
|
|
public function getName()
|
|
{
|
|
return 'global.album_sources.amazon_s3';
|
|
}
|
|
|
|
/**
|
|
* Gets the absolute URL to the given photo file.
|
|
* @param Photo $photo Photo to get the URL to.
|
|
* @param string $thumbnail Thumbnail to get the image to.
|
|
* @return string
|
|
*/
|
|
public function getUrlToPhoto(Photo $photo, $thumbnail = null)
|
|
{
|
|
$client = $this->getClient();
|
|
|
|
if ($this->configuration->s3_signed_urls)
|
|
{
|
|
$cmd = $client->getCommand('GetObject', [
|
|
'Bucket' => $this->configuration->container_name,
|
|
'Key' => $this->getPathToPhoto($photo, $thumbnail)
|
|
]);
|
|
|
|
return (string)$client->createPresignedRequest($cmd, '+5 minutes')->getUri();
|
|
}
|
|
|
|
return $client->getObjectUrl($this->configuration->container_name, $this->getPathToPhoto($photo, $thumbnail));
|
|
}
|
|
|
|
/**
|
|
* Saves a generated thumbnail to its permanent location.
|
|
* @param Photo $photo Photo the image relates to.
|
|
* @param string $tempFilename Filename containing the image.
|
|
* @param string $thumbnail Name of the thumbnail (or null for the original.)
|
|
* @return mixed
|
|
*/
|
|
public function saveThumbnail(Photo $photo, $tempFilename, $thumbnail = null)
|
|
{
|
|
$photoPath = $this->getPathToPhoto($photo, $thumbnail);
|
|
|
|
$uploadAcl = $this->configuration->s3_signed_urls
|
|
? 'private'
|
|
: 'public-read';
|
|
|
|
$this->getClient()->upload($this->configuration->container_name, $photoPath, fopen($tempFilename, 'r+'), $uploadAcl);
|
|
}
|
|
|
|
/**
|
|
* Uploads a new file to the analysis queue specified by queue token.
|
|
*
|
|
* @param string $sourceFilePath Path to the file to upload to the analysis queue
|
|
* @param string $queueToken Queue token to hold the photo
|
|
* @param string $overrideFilename Use a specific filename, or false to set a specific name
|
|
* @return string Path to the file
|
|
*/
|
|
public function uploadToAnalysisQueue($sourceFilePath, $queueToken, $overrideFilename = null)
|
|
{
|
|
$targetPath = sprintf(
|
|
'%s/%s',
|
|
$this->getPathToAnalysisQueue($queueToken),
|
|
is_null($overrideFilename) ? MiscHelper::randomString(20) : $overrideFilename
|
|
);
|
|
|
|
// Note: we don't use "public-read" here as it will not be publicly-accessible, and will be retrieved by an authenticated client
|
|
$this->getClient()->upload($this->configuration->container_name, $targetPath, fopen($sourceFilePath, 'r+'));
|
|
|
|
return $targetPath;
|
|
}
|
|
|
|
private function getClient()
|
|
{
|
|
$config = [
|
|
'credentials' => new \Aws\Credentials\Credentials(
|
|
decrypt($this->configuration->access_key),
|
|
decrypt($this->configuration->secret_key)
|
|
),
|
|
'version' => 'latest',
|
|
'region' => $this->configuration->service_region
|
|
];
|
|
|
|
if (!empty($this->configuration->auth_url) && parse_url($this->configuration->auth_url) !== false)
|
|
{
|
|
$config['endpoint'] = $this->configuration->auth_url;
|
|
}
|
|
|
|
return new \Aws\S3\S3Client($config);
|
|
}
|
|
|
|
private function getOriginalsFolder()
|
|
{
|
|
return '_originals';
|
|
}
|
|
|
|
private function getPathToAnalysisQueue($queueToken)
|
|
{
|
|
return sprintf('analysis-queue/%s', $queueToken);
|
|
}
|
|
|
|
private function getPathToAnalysisQueueItem($queueToken, $fileName)
|
|
{
|
|
return sprintf('%s/%s', $this->getPathToAnalysisQueue($queueToken), $fileName);
|
|
}
|
|
|
|
private function getPathToPhoto(Photo $photo, $thumbnail = null)
|
|
{
|
|
return sprintf(
|
|
'%s/%s/%s',
|
|
$this->album->url_alias,
|
|
is_null($thumbnail) ? $this->getOriginalsFolder() : $thumbnail,
|
|
$photo->storage_file_name
|
|
);
|
|
}
|
|
} |