Resize image and upload to amazon S3 bucket with Laravel and Vue
Published: 2023-03-29 07:49:42



![amazon s3](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/55j9ngkaq6c5kh59h8jx.gif)

In this article, I will teach you how to resize and upload image to amazon s3 bucket with laravel and vuejs.

First we need to install two packages in our Laravel vuejs project
 **Flysystem S3 package** : `composer require league/flysystem-aws-s3-v3 "^3.0"`
**Intervention Image** : `composer require intervention/image`
>The Flysystem S3 package provides an S3 adapter for Flysystem. It easy to store and retrieve files from S3, and to perform operations like moving, copying, and deleting files with this package.
We are using `Intervention Image` library to resize the image.

**Step1:** let's create a bucket on amazon s3.

Search `s3` and click the create button

![s3 amazon](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k7tul9n5iznxc0ekji4e.png)

Give a bucket name, I have given `profile-image-uploader` as a bucket name. You can leave `AWS Region` and `Object Ownership` as default as show in below image

![s3 amazon](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yu4ggl71fg6hm2floi5k.png)

Uncheck *Block all public access* and acknowledge it by checking the checkbox

![s3 amazon](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/top5k1nssc1xxpvd8roo.png)

Enable bucket versioning and click create button. Versioning-enabled buckets can help you recover objects from accidental deletion or overwrite. For example, if you delete an object, Amazon S3 inserts a delete marker instead of removing the object permanently. [Read here](https://docs.aws.amazon.com/AmazonS3/latest/userguide/Versioning.html)

![amazon s3](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r56c8v521fexrw08gufb.png)

Finally, your bucket is created. Now, go to your bucket and create a new folder where you want to store your all images. 

![S3 amazon](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zlqnki23hwky0vzt4342.png)

I will give folder name as `images`

![amazon s3](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xb94pqo4i32nkucqoeu4.png)
Now, click the create button

![amazon s3](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pcmmg99opn1nkj6efc5z.png)

**Step2**: Now, we will attach the `policy` to the `images` folder of our bucket `profile-image-uploader`.
To create the policy, go to [AWS Policy Generator](https://awspolicygen.s3.amazonaws.com/policygen.html)
and follow below steps:
1. Choose s3 Bucket Policy
2. Choose allow for effect
3. Put * for Principal
4. Choose getObject from Actions dropdown

5.For Amazon Resource Name (ARN), use this format 

**arn:aws:s3:::${BucketName}/${KeyName}**
my bucket name is `profile-image-uploader` and `keyName` is folder name which is `images`, So my ARN will be like this
`arn:aws:s3:::profile-image-uploader/images/*`
use `/*` after the folder name which means everything inside the folder images

![amazon s3](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/82k3azcyf1afewj88yli.png)

![amazon s3](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y7452357jgw9vm03umqi.png)

![amazon s3](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pprjjs7yew2chlxslij8.png)

Finally, click generate policy button to get the JSON code,  copy this JSON code as we need this.

![amazon s3](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2khaydakma6klsw8sgs7.png)

Now, go to your bucket, click permissions tab, scroll down to the bucket policy. Here click edit, paste the JSON code and finally click the save button.

![amazon s3](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uyqjqhflp3hwaamx4194.png)

![amazon s3](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y9sef4w737adrhf8htf8.png)

**Step3**: Now in this part we will create a user and get the` keys`

First search `IAM`, click IAM to go Identity and Access Management (IAM) dashboard.

![Iam amazon s3](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3jxn7l8lkfkie0h5yodc.png)
On the left side, you will see the Access management, click the `users` from the list.

Give the username, click the radio button *I want to create an IAM user*, and click the next button to go next page.

![amazon s3](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bp7gnzyc3t3rsxfuu4mt.png)

Now, on the next page set the permission. First, choose the third option *Attach policies directly*. Search for `AmazonS3FullAccess` and check it as shown below in the image.

![amazon s3](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oqb2u9rjvkk6rutb1k5w.png)

![amazon s3](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mp5sp9badbh2dk2djt4s.png)
![amazon s3](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qtel0x9qro0a5iis48h1.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2v2ijo4ts1st1w8ezs1h.png)

Click the user and go to `Security Credentails` tab

![amazon s3](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iva9mn62p0tquioir0bs.png)
Scroll down and click the access key

![amazon s3](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/npgl1s3izi56fbu85k3p.png)

On the next page do as shown in the image below and click the next button.
![amazon s3](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pa2gme4emv6oyrk7u2hy.png)

Now copy your `access key` and `Secret access key` and save it somewhere as we need this.

![amazon s3](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fzh94gjjd4z26ksnn6if.png)

That's all with S3 bucket. Now it's time to make an `API` with Laravel.

First, we will make a column name `profile_picture` in the `users` table. Open the `users` migration file and add the string column `profile_picture` as shown below in the image.

![Laravel Image upload](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2jjpqsdb9xm8n1k9vfgl.png)

Now, run the migration with this command from your project directory `php artisan migrate`
Open you `.env` file and set the key values like this

```
AWS_ACCESS_KEY_ID=************
AWS_SECRET_ACCESS_KEY=***************
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=profile-image-uploader
AWS_USE_PATH_STYLE_ENDPOINT=false
```

You should have `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` while creating the user.

If you open `filesystems.php` file from `config` folder you will see `s3` setup made by laravel.

![s3](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pne26j7cx0ac4cytfyuu.png)

Open you `web.php` and create two routes as below

```php
Route::get('upload',[UserController::class, 'create']);

Route::post('upload/to/s3', [UserController::class, 'uploadImageToS3']);
```

The first route will be responsible for rendering a view `upload.blade.php` and the second will be responsible for processing a file. Now, I will make a `upload.blade.php` and load a vue js component `Upload.vue` in our view `upload.blade.php`

```php
    public function index()
    {
        return view('upload'); 
    }
```
This will be my Upload vue js component 
`Upload.vue`
```js
<template>
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-body">
                    <div class="image-container">
                        <input type="file" ref="myImage" id="image-upload" class="form-control"
                            @change="updateProfilePicture()">
                        <p v-if="status">Uploading...<br>
                        <div class="progress">
                            <div class="progress-bar" role="progressbar" :style="{ width: `${progress}%` }"
                                :aria-valuenow="progress" aria-valuemin="0" aria-valuemax="100">
                                {{ progress }}%
                            </div>
                        </div>
                        </p>
                        <br>
                        <p id="errorUploadingImage"></p>
                        <img class="mx-auto d-block" :src="profileImage">
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>

<script setup>
import { ref } from 'vue'
const progress = ref('')
const status = ref(false)
const profileImage = ref('')
const myImage = ref(null)

const updateProfilePicture = () => {}

</script>
```
This is our vue upload component
![Vue js upload](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i0hl5pw0ebm9oggv2k1i.png)
>This is a Vue template and we have a card with an input field for uploading an image, a progress bar to indicate the status of the upload, and an image container to display the uploaded image. The template uses the `v-if` directive to show the progress bar and the `"Uploading..."` message only if status is true. status is a reactive variable defined in the script section of the component. The input field for uploading an image is defined using the input HTML element with a type of `"file"` and a reference to the element using `ref="myImage"`. The `@change` event listener is used to call the `updateProfilePicture` method when a file is selected for upload. The progress bar is defined using the Bootstrap progress-bar class with a dynamic width that is bound to the progress reactive variable. The image container is defined using the img HTML element with a src attribute that is bound to the profileImage reactive variable. `profileImage` is initially an empty string but is updated with the `URL` of the uploaded image once the upload is complete. Finally, there is an empty `p` element with an `ID` of `errorUploadingImage` that is used to display any errors that occur during the upload process.

Now, lets process image within a `updateProfilePicture`

```js
<script setup>
import { ref } from 'vue'
const progress = ref('')
const status = ref(false)
const profileImage = ref('')
const myImage = ref(null)

const updateProfilePicture = () => {
    status.value = true
    const image = myImage.value.files[0];
    const formData = new FormData();
    formData.append('image', image)
    axios.post('/upload/to/s3', formData, {
        headers: {
            'Content-Type': 'multipart-formdata'
        },
        onUploadProgress: (progressEvent) => {
            progress.value = Math.round(
                (progressEvent.loaded * 100) / progressEvent.total
            );
        },
    }).then((response) => {
        status.value = false
        profileImage.value = response.data.url
    }).catch((error) => {
        document.getElementById('errorUploadingImage').innerText = 'Error in uploading image';
        console.log(error)
    })
}

</script
```
>Inside `updateProfilePicture` function, I have set the status variable to true, indicating that an upload is in progress. I then created a `new FormData` object and appends the selected image file to it with the key 'image'. This FormData object will be used to send the file to the server using a POST request. The code then uses Axios to send the POST request to the URL `/profileImage/upload`. The formData object is passed as the second parameter to the post() method. It also sets the `'Content-Type'` header to `'multipart-formdata'`. The `onUploadProgress` option is used to track the upload progress. It updates the progress variable, which is bound to the progress bar in the Vue template, with the percentage of the file that has been uploaded. If the request is successful, the function sets the status variable to false, indicating that the upload is complete, and sets the profileImage variable to the URL of the uploaded image returned in the response data. If there is an error, it displays an error message on the page and logs the error to the console.

Now, let's work on uploadImageToS3 method of UserController to upload the file to aws s3 bucket.

   

```php
 public function uploadImageToS3(Request $request): JsonResponse
 {
        $request->validate([
            'image' => 'required|image|mimes:jpeg,png,jpg|max:2048',
        ]);

        if ($request->hasFile('image')) {
            //extracts the uploaded file
            $image = $request->file('image');
          //generates a unique filename for it using hashName()
            $filename = $image->hashName();

            // Resize the image to a maximum width of 150 pixels, this is form Intervention Image library
            $img = Image::make($image->getRealPath())->resize(150, null, function ($constraint) {
                $constraint->aspectRatio();
            });

            // Upload the resized image to S3
            $path = 'images/' . $filename;
            Storage::disk('s3')->put($path, $img->stream()->__toString());

            // Update the user's profile_picture with the S3 object URL
            User::where('id', auth()->id())->update([
                'profile_picture' =>  $awsPath = Storage::disk('s3')->url($path)
            ]);

            return response()->json(['url' => $awsPath], Response::HTTP_OK);

        }
  }

```
>Above I have written a comment for each line of code to make it easy for you to understand. The function returns a JsonResponse, make sure you use Illuminate\Http\JsonResponse; The function validates the image file uploaded with the request to ensure that it is an image file (jpeg, png, or jpg), is no larger than 2048KB, and is required for the request to be considered valid. After validating the image, we resize the image with Intervention Image Library and uploaded the resized image to the s3 bucket.`$img->stream()->__toString()` is used to get the raw image data as a string, which is then passed to the `put()` method of the `S3` disk driver to upload the image. Finally, we will update the user `profile_picture` column with `S3` Image URL which will be something like this
`https://profile-image-uploader.s3.amazonaws.com/images/01WKdCYuBFSdQfwCENxz5eECU4jsW7xWxqfjrLUr.jpg`

Thank you for reading.I hope you enjoyed it!