From 38c9cb2674d0fb784b8f7f43144ccb528b9a64da Mon Sep 17 00:00:00 2001 From: Guillaume Sainthillier Date: Wed, 25 Oct 2023 17:40:18 +0200 Subject: [PATCH] try to reduce AWS bill by using cloudfront to download upload --- config/packages/flysystem.yaml | 25 +++-- src/Flysystem/HttpClientAdapter.php | 140 ++++++++++++++++++++++++++++ 2 files changed, 156 insertions(+), 9 deletions(-) create mode 100644 src/Flysystem/HttpClientAdapter.php diff --git a/config/packages/flysystem.yaml b/config/packages/flysystem.yaml index 95593801..216944dd 100644 --- a/config/packages/flysystem.yaml +++ b/config/packages/flysystem.yaml @@ -1,24 +1,31 @@ # Read the documentation at https://github.com/thephpleague/flysystem-bundle/blob/master/docs/1-getting-started.md + +services: + app.flysytem.http_client_adapter: + class: App\Flysystem\HttpClientAdapter + autowire: true + arguments: + $options: { base_uri: '%env(AWS_S3_URL)%' } + flysystem: storages: s3.storage.reader: - adapter: 'aws' + adapter: 'app.flysytem.http_client_adapter' options: - client: 'Aws\S3\S3Client' # The service ID of the Aws\S3\S3Client instance - bucket: "%env(AWS_S3_BUCKET_NAME)%" + bucket: '%env(AWS_S3_BUCKET_NAME)%' users.storage: adapter: 'aws' options: client: 'Aws\S3\S3Client' # The service ID of the Aws\S3\S3Client instance - bucket: "%env(AWS_S3_BUCKET_NAME)%" + bucket: '%env(AWS_S3_BUCKET_NAME)%' prefix: 'uploads/users' events.storage: adapter: 'aws' options: client: 'Aws\S3\S3Client' # The service ID of the Aws\S3\S3Client instance - bucket: "%env(AWS_S3_BUCKET_NAME)%" + bucket: '%env(AWS_S3_BUCKET_NAME)%' prefix: 'uploads/documents' thumbs.storage: @@ -33,7 +40,7 @@ flysystem: # In production we disable thumb storage because it's handled by cloudfront when@prod: - flysystem: - storages: - thumbs.storage: - adapter: 'memory' + flysystem: + storages: + thumbs.storage: + adapter: 'memory' diff --git a/src/Flysystem/HttpClientAdapter.php b/src/Flysystem/HttpClientAdapter.php new file mode 100644 index 00000000..26651314 --- /dev/null +++ b/src/Flysystem/HttpClientAdapter.php @@ -0,0 +1,140 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace App\Flysystem; + +use League\Flysystem\Config; +use League\Flysystem\FileAttributes; +use League\Flysystem\FilesystemAdapter; +use League\Flysystem\UnableToCheckDirectoryExistence; +use League\Flysystem\UnableToCopyFile; +use League\Flysystem\UnableToCreateDirectory; +use League\Flysystem\UnableToDeleteDirectory; +use League\Flysystem\UnableToDeleteFile; +use League\Flysystem\UnableToListContents; +use League\Flysystem\UnableToMoveFile; +use League\Flysystem\UnableToReadFile; +use League\Flysystem\UnableToRetrieveMetadata; +use League\Flysystem\UnableToSetVisibility; +use League\Flysystem\UnableToWriteFile; +use Symfony\Component\HttpClient\Response\StreamableInterface; +use Symfony\Contracts\HttpClient\Exception\ExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; +use Symfony\Contracts\HttpClient\HttpClientInterface; + +class HttpClientAdapter implements FilesystemAdapter +{ + private readonly HttpClientInterface $httpClient; + + public function __construct( + HttpClientInterface $httpClient, + array $options = [] + ) { + $this->httpClient = $httpClient->withOptions($options); + } + + public function fileExists(string $path): bool + { + return true; + } + + public function directoryExists(string $path): bool + { + throw new UnableToCheckDirectoryExistence('This is a readonly adapter.'); + } + + public function write(string $path, string $contents, Config $config): void + { + throw UnableToWriteFile::atLocation($path, 'This is a readonly adapter.'); + } + + public function writeStream(string $path, $contents, Config $config): void + { + throw UnableToWriteFile::atLocation($path, 'This is a readonly adapter.'); + } + + public function read(string $path): string + { + try { + return $this->httpClient->request('GET', $path)->getContent(); + } catch (TransportExceptionInterface|ExceptionInterface $exception) { + throw UnableToReadFile::fromLocation($path, $exception->getMessage(), $exception); + } + } + + public function readStream(string $path) + { + try { + $response = $this->httpClient->request('GET', $path); + if (!$response instanceof StreamableInterface) { + throw UnableToReadFile::fromLocation($path, 'Response is not streamable.'); + } + + return $response->toStream(); + } catch (TransportExceptionInterface|ExceptionInterface $exception) { + throw UnableToReadFile::fromLocation($path, $exception->getMessage(), $exception); + } + } + + public function delete(string $path): void + { + throw UnableToDeleteFile::atLocation($path, 'This is a readonly adapter.'); + } + + public function deleteDirectory(string $path): void + { + throw UnableToDeleteDirectory::atLocation($path, 'This is a readonly adapter.'); + } + + public function createDirectory(string $path, Config $config): void + { + throw UnableToCreateDirectory::atLocation($path, 'This is a readonly adapter.'); + } + + public function setVisibility(string $path, string $visibility): void + { + throw UnableToSetVisibility::atLocation($path, 'This is a readonly adapter.'); + } + + public function visibility(string $path): FileAttributes + { + throw UnableToRetrieveMetadata::visibility($path, 'This is a readonly adapter.'); + } + + public function mimeType(string $path): FileAttributes + { + throw UnableToRetrieveMetadata::mimeType($path, 'This is a readonly adapter.'); + } + + public function lastModified(string $path): FileAttributes + { + throw UnableToRetrieveMetadata::lastModified($path, 'This is a readonly adapter.'); + } + + public function fileSize(string $path): FileAttributes + { + throw UnableToRetrieveMetadata::fileSize($path, 'This is a readonly adapter.'); + } + + public function listContents(string $path, bool $deep): iterable + { + throw UnableToListContents::atLocation($path, 'This is a readonly adapter.'); + } + + public function move(string $source, string $destination, Config $config): void + { + throw new UnableToMoveFile("Unable to move file from $source to $destination as this is a readonly adapter."); + } + + public function copy(string $source, string $destination, Config $config): void + { + throw new UnableToCopyFile("Unable to copy file from $source to $destination as this is a readonly adapter."); + } +}