Laravel Jetstream provides a lot of great functionality out of the box and although I don't really use the front-end it provides, which is nice, I do like to use the actions, traits, and some of the backend tools it provides. One problem I ran into just recently was trying to use Amazon S3 as the storage for profile pictures. Based on the documentation that I could find it looked like you just have to update the jetstream.php config file and change profile_photo_disk from local to s3
'profile_photo_disk' => 's3' ,
Additionally, you need to update your .env file to include the Amazon s3 attributes.
# Optionally Set the default filesystem driver to S3FILESYSTEM_DISK =s3
# Add items needed for S3-based filesystem to workAWS_ACCESS_KEY_ID =AKIASQG22xxxxxxxxxAWS_SECRET_ACCESS_KEY =vPnZwGPuCxxxxxxxx/xxxxxxxxxxxxxxxxAWS_DEFAULT_REGION =us-east-1AWS_BUCKET =test-bucketAWS_USE_PATH_STYLE_ENDPOINT =false
That should have been it. But, Nope!
So after digging around on the internet, I found this post on stackoverflow, here. At the time of this post there were 3 answers and all roughly had the same response except answer 3 gave one extra piece of import information related to S3 buckets. ( I will get into that in a bit). The answer I found most useful looked like this:
public function updateProfilePhoto(UploadedFile $photo, $storagePath = 'profile-photos') { tap($this->profile_photo_path, function ($previous) use ($photo, $storagePath) { $this->forceFill([ 'profile_photo_path' => $photo->store($storagePath, $this->profilePhotoDisk()), ])->save(); if ($previous) { Storage::disk($this->profilePhotoDisk())->delete($previous); } }); } public function getTemporaryProfilePhotoUrl() { if ($this->profile_photo_path) { return Storage::disk($this->profilePhotoDisk())->temporaryUrl( $this->profile_photo_path, now()->addHours(1) // La URL expirará en una hora ); } else { return $this->defaultProfilePhotoUrl(); } }
The most important change to the first function is changing the $photo→storePublicly to $photo→store. This change will now use the configured disk that we set up above to store the profile photo rather than defaulting to local storage. Yeah, it worked! Well, sort of? Now my images are being stored in the S3 bucket but I do not have the correct permissions to access them. That is where the second function in this answer comes into play. The second function gives you a signed url so that the image is accessible for 1 hour, I am assuming that can be adjusted ( I have not tried). So the one additional step I took to make this signed url available to my API for the user, was to add the following to the user model file ( because the functions above were already added to the user model class as a trait)
protected $appends = ['profile_photo_url' , 'temporary_profile_photo_url'];
Now every time the user model is requested it will provide the temporary photo URL as well, if there is one. Now, YEAH, everything works as expected and the S3 bucket is not open to the world.