diff --git a/composer.json b/composer.json index 5c696ef..c9dc8b1 100644 --- a/composer.json +++ b/composer.json @@ -13,7 +13,8 @@ "php": ">=5.4", "symfony/http-foundation": "~2.3|~3.0", "imagine/imagine": "~0.6.2", - "doctrine/inflector": "~1" + "doctrine/inflector": "~1", + "superbalist/flysystem-google-storage": "^3.0|^4.0|^5.0" }, "require-dev": { "mockery/mockery": "0.8.0", diff --git a/src/Attachment.php b/src/Attachment.php index c2c29ab..68321c0 100644 --- a/src/Attachment.php +++ b/src/Attachment.php @@ -532,15 +532,10 @@ public function jsonSerialize() */ protected function flushWrites() { - foreach ($this->queuedForWrite as $style) { - if ($style->dimensions && $this->uploadedFile->isImage()) { - $file = $this->resizer->resize($this->uploadedFile, $style); - } else { - $file = $this->uploadedFile->getRealPath(); - } - - $filePath = $this->path($style->name); - $this->move($file, $filePath); + if ($this->uploadedFile->isImage()) { + $this->queueImages(); + } else { + $this->queueFile(); } $this->queuedForWrite = []; @@ -555,6 +550,27 @@ protected function flushDeletes() $this->queuedForDeletion = []; } + protected function queueImages() + { + foreach ($this->queuedForWrite as $style) { + if ($style->dimensions) { + $file = $this->resizer->resize($this->uploadedFile, $style); + } else { + $file = $this->uploadedFile->getRealPath(); + } + + $filePath = $this->path($style->name); + $this->move($file, $filePath); + } + } + + protected function queueFile() + { + $file = $this->uploadedFile->getRealPath(); + $filePath = $this->path($this->config->default_style); + $this->move($file, $filePath); + } + /** * Fill the queuedForWrite que with all of this attachment's styles. */ diff --git a/src/Factories/Storage.php b/src/Factories/Storage.php index ecf6d63..b88b2bd 100644 --- a/src/Factories/Storage.php +++ b/src/Factories/Storage.php @@ -4,6 +4,7 @@ use Codesleeve\Stapler\Attachment as AttachedFile; use Codesleeve\Stapler\Storage\Filesystem; +use Codesleeve\Stapler\Storage\GCS; use Codesleeve\Stapler\Storage\S3; use Codesleeve\Stapler\Stapler; @@ -29,6 +30,12 @@ public static function create(AttachedFile $attachment) return new S3($attachment, $s3Client); break; + case 'gcs': + $gcsFilesystem = Stapler::getGCSClientInstance($attachment); + + return new GCS($attachment, $gcsFilesystem); + break; + default: return new Filesystem($attachment); break; diff --git a/src/Stapler.php b/src/Stapler.php index a2c3588..21e399a 100755 --- a/src/Stapler.php +++ b/src/Stapler.php @@ -5,6 +5,9 @@ use Codesleeve\Stapler\Interfaces\Attachment as AttachmentInterface; use Codesleeve\Stapler\Interfaces\Config as ConfigInterface; use Codesleeve\Stapler\File\Image\Resizer; +use League\Flysystem\Filesystem as LeagueFilesystem; +use Superbalist\Flysystem\GoogleStorage\GoogleStorageAdapter; +use Google\Cloud\Storage\StorageClient; use Aws\S3\S3Client; /** @@ -187,6 +190,26 @@ public static function getS3ClientInstance(AttachmentInterface $attachedFile) return static::$s3Clients[$key]; } + /** + * Return an Filesystem object for Google Cloud Storage. + * + * @param AttachmentInterface $attachedFile + * + * @return \League\Flysystem\Filesystem + */ + public static function getGCSClientInstance(AttachmentInterface $attachedFile) + { + $storageClient = new StorageClient([ + 'projectId' => $attachedFile->google_cloud_project_id, + 'keyFilePath' => $attachedFile->google_cloud_key_file, + ]); + + $bucket = $storageClient->bucket($attachedFile->google_cloud_storage_bucket); + $adapter = new GoogleStorageAdapter($storageClient, $bucket); + $filesystem = new LeagueFilesystem($adapter); + return $filesystem; + } + /** * Return a configuration object instance. * If no instance is currently set, we'll return an instance diff --git a/src/Storage/GCS.php b/src/Storage/GCS.php new file mode 100644 index 0000000..4d7982b --- /dev/null +++ b/src/Storage/GCS.php @@ -0,0 +1,85 @@ +attachedFile = $attachedFile; + $this->filesystem = $filesystem; + } + + /** + * Return the url for a file upload. + * + * @param string $styleName + * + * @return string + */ + public function url($styleName) + { + return $this->filesystem->getAdapter()->getUrl($this->path($styleName)); + } + + /** + * Return the key the uploaded file object is stored under within a bucket. + * + * @param string $styleName + * + * @return string + */ + public function path($styleName) + { + return $this->attachedFile->getInterpolator()->interpolate($this->attachedFile->path, $this->attachedFile, $styleName); + } + + /** + * Remove an attached file. + * + * @param array $filePaths + */ + public function remove(array $filePaths) + { + if ($filePaths) { + $this->filesystem->delete($filePaths); + } + } + + /** + * Move an uploaded file to it's intended destination. + * + * @param string $file + * @param string $filePath + */ + public function move($file, $filePath) + { + $this->filesystem->put($filePath, fopen($file, 'r+')); + + unlink($file); + } +} diff --git a/src/Validator.php b/src/Validator.php index 4f15102..5c73e42 100644 --- a/src/Validator.php +++ b/src/Validator.php @@ -14,7 +14,17 @@ class Validator implements ValidatorInterface */ public function validateOptions(array $options) { - $options['storage'] == 'filesystem' ? $this->validateFilesystemOptions($options) : $this->validateS3Options($options); + switch ($options['storage']) { + case 's3': + $this->validateS3Options($options); + break; + case 'gcs': + $this->validateGCSOptions($options); + break; + case 'filesystem': + default: + $this->validateFilesystemOptions($options); + } } /** @@ -54,4 +64,27 @@ protected function validateS3Options(array $options) throw new Exceptions\InvalidUrlOptionException('Invalid Path: a key is required for s3 storage.', 1); } } + + /** + * Validate the attachment options for an attachment type when the storage + * driver is set to 'gcs'. + * + * @throws InvalidUrlOptionException + * + * @param array $options + */ + protected function validateGCSOptions(array $options) + { + if (!$options['google_cloud_project_id']) { + throw new Exceptions\InvalidUrlOptionException('Invalid Path: a google project id is required for gcs storage.', 1); + } + + if (!$options['google_cloud_key_file']) { + throw new Exceptions\InvalidUrlOptionException('Invalid Path: a google key file is required for gcs storage.', 1); + } + + if (!$options['google_cloud_storage_bucket']) { + throw new Exceptions\InvalidUrlOptionException('Invalid Path: a bucket is required for gcs storage.', 1); + } + } }