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 ); } }