blue-twilight/app/AlbumSources/AmazonS3Source.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
);
}
}