Skip to content

Commit

Permalink
try to reduce AWS bill by using cloudfront to download upload
Browse files Browse the repository at this point in the history
  • Loading branch information
guillaume-sainthillier committed Oct 25, 2023
1 parent 554dde4 commit 38c9cb2
Show file tree
Hide file tree
Showing 2 changed files with 156 additions and 9 deletions.
25 changes: 16 additions & 9 deletions config/packages/flysystem.yaml
Original file line number Diff line number Diff line change
@@ -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:
Expand All @@ -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'
140 changes: 140 additions & 0 deletions src/Flysystem/HttpClientAdapter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
<?php

/*
* This file is part of By Night.
* (c) 2013-present Guillaume Sainthillier <[email protected]>
*
* 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.");
}
}

0 comments on commit 38c9cb2

Please sign in to comment.