'Laravel s3 upload file with metadata using pre-signed url

I am implementing S3 file uploads and downloads using pre-signed urls. I have one s3 bucket (versioning enabled) and one AWS user but I want to track the history of each file in terms of which of my application users modified the file.

I have a versioned S3 bucket and my thought is that I can append metadata to every file to identify my application user and possibly other data too.

Here is my code:

    public function presignedUpload(Request $request)
    {
        $this->validate($request, [
            'name' => 'string|required'
        ]);

        $s3 = Storage::disk('s3');
        $client = $s3->getDriver()->getAdapter()->getClient();
        $expiry = "+10 minutes";


        $options = ['user-data' => 'user-meta-value'];

        $cmd = $client->getCommand('PutObject', [
            'Bucket' => \Config::get('filesystems.disks.s3.bucket'),
            'Key' => 'path/to/file/' . $request->name,
            'ACL' => 'public-read',
        ], $options);

        $request = $client->createPresignedRequest($cmd, $expiry);

        $presignedUrl = (string)$request->getUri();

        return response()->json(['url' => $presignedUrl], 201);
    }

I deduced that I can pass an $options array from the FilesystemAdapter::class. This code does upload the file, but the metadata in AWS looks empty.

Is my $options array in the wrong format?

Appreciate any help on this.



Solution 1:[1]

Metadata can be uploaded like this:

 public function presignedUpload(Request $request)
    {
        $this->validate($request, [
            'name' => 'string|required'
        ]);

        $s3 = Storage::disk('s3');
        $client = $s3->getDriver()->getAdapter()->getClient();
        $expiry = "+10 minutes";


        $options = ['user-data' => 'user-meta-value'];

        $cmd = $client->getCommand('PutObject', [
            'Bucket' => \Config::get('filesystems.disks.s3.bucket'),
            'Key' => 'path/to/file/' . $request->name,
            'ACL' => 'public-read',
            'Metadata' => $options,
        ]);

        $request = $client->createPresignedRequest($cmd, $expiry);

        $presignedUrl = (string)$request->getUri();

        return response()->json(['url' => $presignedUrl], 201);
    }

Solution 2:[2]

The selected answer doesn't work anymore after Laravel 9. Flysystem recommends you to use S3 SDK instead of Storage driver.

https://github.com/thephpleague/flysystem/issues/1423

Use the S3 SDK instead to create a resigned upload URL.

    $uploadDisk = 's3';

    $s3Client = new S3Client([
        'credentials' => [
            'key' => config('filesystems.disks.' . $uploadDisk . '.key'),
            'secret' => config('filesystems.disks.' . $uploadDisk . '.secret'),
        ],
        'region' => config('filesystems.disks.' . $uploadDisk . '.region'),
        'version' => 'latest',
    ]);
    
    $s3Bucket = config('filesystems.disks.' . $uploadDisk . '.bucket');
    $s3Key = \Illuminate\Support\Str::uuid(); // your file name
    $s3Options = [];
    
    $command = $s3Client->getCommand('PutObject', [
        'Bucket' => $s3Bucket,
        'Key' => $s3Key,
        'MetaData' => $s3Options,
    ]);
    
    $request = $s3Client->createPresignedRequest($command, '+20 minutes');
    
    $url = (string) $request->getUri();

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 reans
Solution 2 shanecp