Get S3Client from storage facade in Laravel 9 - php

I am trying to upgrade an S3 Multipart Uploader from Laravel 8 to Laravel 9 and have upgraded to Flysystem 3 as outlined in the documentation and have no dependency errors https://laravel.com/docs/9.x/upgrade#flysystem-3.
I am having trouble getting access to the underlying S3Client to create a Multipart upload.
// Working in Laravel 8
// Laravel 9 throws exception on getAdapter()
$client = Storage::disk('s3')->getDriver()->getAdapter()->getClient();
// Underlying S3Client is used to create Multipart uploader as below
$bucket = config('filesystems.disks.s3.bucket');
$result = $client->createMultipartUpload([
'Bucket' => $bucket,
'Key' => $key,
'ContentType' => 'image/jpeg',
'ContentDisposition' => 'inline',
]);
return response()
->json([
'uploadId' => $result['UploadId'],
'key' => $result['Key'],
]);
Laravel 9, however, throws an exception Call to undefined method League\Flysystem\Filesystem::getAdapter().
I've looked over the source for League\Flysystem and updates to Laravel but can't seem to figure out the right way to work with the updates and get access to the underlying Aws\S3\S3Client.
My larger project is using a forked laravel-uppy-s3-multipart-upload library which can be seen here
https://github.com/kerkness/laravel-uppy-s3-multipart-upload/tree/laravel9

This was discussed in this Flysystem AWS adapter github issue:
https://github.com/thephpleague/flysystem-aws-s3-v3/issues/284
A method is being added in Laravel, and will be released next Tuesday (February 22, 2022):
https://github.com/laravel/framework/pull/41079
Workaround
The Laravel FilesystemAdapter base class is macroable, which means you could do this in your AppServiceProvider:
Illuminate\Filesystem\AwsS3V3Adapter::macro('getClient', fn() => $this->client);
Now you can call...
Storage::disk('s3')->getClient();
... and you will have an instance of the S3Client. (I love macros!)
You can remove this macro once next Tuesday's release is available.

Get / Fetch object / Image from S3 in Laravel 9? OR
*** Display files from S3 in Laravel ? ***
Ans:
These are only three steps to get object like ( Image, PDF, Video ) any kind of attachment from Laravel 9:
Go to project directory:
Step #1: composer require league/flysystem-aws-s3-v3
Step #2 Go to .env and right this
AWS_ACCESS_KEY_ID=your_aws_access_key
AWS_SECRET_ACCESS_KEY=your_aws_secret_key
AWS_DEFAULT_REGION=your_aws_regios
AWS_BUCKET=your_bucket_name
AWS_USE_PATH_STYLE_ENDPOINT=false
Step #3: Goto any controller and right this in the function
$source = Storage::disk('s3')->temporaryUrl($item->path, now()->addMinutes(30));
And then you can pass $source variable to wherever you want.
Thanks
Jahanzaib

Related

AWS facade in Laravel fails to instantiate CloudSearch class properly

I am using the AWS facade for Laravel and I can instantiate a CloudSearchDomainClient object like this:
$c = AWS::createClient('cloudsearchdomain', ['endpoint' => '{our-endpoint}']);
But when I attempt to search like so:
$c->search(['query' => 'test']);
I get this error: Aws\CloudSearchDomain\Exception\CloudSearchDomainException with message 'Error executing "Search" on "2013-01-01/search"; AWS HTTP error: cURL error 6: Could not resolve host: 2013-01-01
It thinks the version is the endpoint.
I have the proper .env vars, eg. AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY and AWS_DEFAULT_REGION. I am able to use other AWS services, but CloudSearch specifically is a problem. What am I doing incorrectly?
I think you are mixing two things. An endpoint is a domain and api version is a date. So it should be more like this:
$c = AWS::createClient(
'cloudsearchdomain',
[
'endpoint' => 'https://example.com',
'apiVersion' => '2013-01-01',
]
);

Test error handling of the PHP S3Client uploadAsync method

Method uploadAsync automatically decides and uploads object in single or multiple chunks. And we can expect 2 different kind of expectations S3Exception and S3MultipartUploadException in our $result['reason']. I'm trying to mock part of the S3 client to throw the exception, I have done this by MockHandler:
$s3->getHandlerList()->setHandler(new MockHandler([$result], null, null));
And the $result is:
new S3Exception("", new Command("mockCommand"), [
'code' => 'mockCode',
'response' => new Response(401)
])
Or either this:
new S3MultipartUploadException(new UploadState(['testid']))
Since the S3MultipartUploadException is not instance of AwsException I got this exception:
InvalidArgumentException: Expected an Aws\ResultInterface or Aws\Exception\AwsException.
How can I handle such scenario?
I have asked this on the project repo and they confirmed this is a bug https://github.com/aws/aws-sdk-php/issues/2143.

Listing buckets in cloud storage project using PHP API

I'm trying to list out all (some, even) of the buckets in my storage project. If I know the name of a bucket, the "bucket" function will get the bucket. But I can't use "buckets" to list the buckets in my project:
$client = new StorageClient(
[
'projectId' => <my project id>,
'keyFile' => json_decode(file_get_contents(<my json file>))
]
);
$bucket_name = 'idx-mls-info-gs-ihouseprd.com';
$one_bucket = $client->bucket( $bucket_name );
print "GOT BUCKET: " . $one_bucket->name() . "\n";
// NOTE: this works
$prefix = 'idx-';
print "Getting buckets (prefix: $prefix)\n";
$buckets = $client->buckets( ['prefix' => $prefix] );
foreach ( $buckets as $bucket )
{
printf('Bucket: %s' . PHP_EOL, $bucket->name());
}
print "done with buckets"
// THIS DOES NOTHING
My service account has the "Storage Admin" role. I am perplexed.
NOTE: I am using PHP 5.6, in case that matters. Composer didn't have a problem installing the GCS library, so I assumed that was ok.
Ok, so I must be missing something. Using my test case of getting a single bucket, I have then used $one_bucket->object(), and successfully gotten an object. But if I try $one_bucket->objects(), I again get nothing. So the multiple case for entities in the GCS is not working for me, whether buckets or objects. Perhaps that's a clue.
Further information:
There is an exception when hitting the iterator (foreach $buckets as $bucket):
exception 'Exception' with message 'exception 'Google\Cloud\Core\Exception\ServiceException' with message '4096:Argument 2 passed to Google\Auth\CredentialsLoader::makeCredentials() must be of the type array, object given, called in /home/httpd/idxv3/vendor/google/cloud-core/src/RequestWrapperTrait.php on line 158 and defined in /home/httpd/idxv3/vendor/google/auth/src/CredentialsLoader.php on line 135' in /home/httpd/idxv3/vendor/google/cloud-core/src/RequestWrapper.php:362
Not sure why the authentication is causing problems.
I've found a link for your first question and I hope this helps guide you. It describes how to list the buckets in your project.
Ok, I found out what the problem was/is. It is in the creation of the storage client. My call the json_decode was missing a parameter. As in my original code, what gets passed into the constructor is a stdClass Object, which is not liked down in the depths of the code. Adding ", true" to the call to json_decode, what gets passed in is then an array, which is desired:
$client = new StorageClient(
[
'projectId' => <my project id>,
'keyFile' => json_decode(file_get_contents(<my json file>), true)
]
);
This fixes the problems I was having. Not sure why I didn't get errors earlier on, like in the constructor.

Pass Parameter from controller to Component in Cakephp

I have been given a task to maintain a cakephp app. There is an issue with uploading files to server. All these time we were using AWS S3 Bucket now we want upload it to our own file server. The coding part was done by my ex-colleague.
I am trying a simple stuff I want to send fileName from my controller to Component files called S3.
Coding which is done is like this:
$this->S3->uploadFile($this->request->data('upload.tmp_name'),$fileKey);
In S3Component file:
I have written the following:
public function uploadFile($filePath, $fileKey, $metaData = [])
{
$fileUtility = new FileUtility(1024 * 1024, array("ppt", "pdf"));
return $fileUtility->uploadFile($_FILES[$fileKey], $filePath);
}
Now how do I pass the values in S3->uploadFile correctly to reflect the uploadfile function S3Compontent file.
Thanks!
If you can be bothered refactoring, you could try Flysystem, a file management package installable through composer. https://flysystem.thephpleague.com/
The nice thing about it is, you just swap an S3 adapter for a local adapter, ftp adapter, etc (there are a few!), and none of your code needs to change!
For instance:
use Aws\S3\S3Client;
use League\Flysystem\AwsS3v3\AwsS3Adapter;
use League\Flysystem\Filesystem;
$client = S3Client::factory([
'credentials' => [
'key' => 'your-key',
'secret' => 'your-secret',
],
'region' => 'your-region',
'version' => 'latest|version',
]);
$adapter = new AwsS3Adapter($client, 'your-bucket-name', 'optional/path/prefix');
And to create a Local Adapter:
use League\Flysystem\Filesystem;
use League\Flysystem\Adapter\Local;
$adapter = new Local(__DIR__.'/path/to/root');
$filesystem = new Filesystem($adapter);
Then, you can make identical calls to $filesystem like these:
$filesystem->write('path/to/file.txt', 'contents');
$contents = $filesystem->read('path/to/file.txt');
$filesystem->delete('path/to/file.txt');
$filesystem->copy('filename.txt', 'duplicate.txt');
Check out the API here: https://flysystem.thephpleague.com/api/

Listing objects filtered by prefix in AWS with PHP SDK

I recently got the task to manage data which is stored via the Amazone Web Service.
According to the docu of Amazone i tried the following code to list all Objects within a bucket and it works fine:
$aws = Aws::factory('/path/to/my/config.php');
$s3 = $aws->get('s3');
$it = $s3->getIterator('ListObjects', array (
'Bucket' => 'myBucket',
)
);
foreach($it as $o){
echo $o['Key']."<br />";
}
But i need to list all objects only with a certain prefix. To achieve this i added the following line below line 6 of the shown code:
'prefix' => 'myPrefix/',
(The actual key of the file i want to access is (scheme):
myPrefix/subPrefix/subPrefix2/file.txt)
But the code keeps returning all objects in the bucket.
I havn't found any helpfull hints in the Amazon docu for my question.
Can anyone tell me (one of) the correct syntax to list all objects of a given prefix in PHP?
thank you in advance for any help
According to the following thread ...
List objects in a specific folder on Amazon S3
... you need capitalized the index-values of the array that is passed as second argument to the getIterator-function:
'Prefix' => 'myPrefix/',

Categories