DEV Community

Cover image for Creating a cool REST profile avatar uploader with Laravel, Vue and Axios.
James Sinkala
James Sinkala

Posted on • Edited on • Originally published at jamesinkala.com

Creating a cool REST profile avatar uploader with Laravel, Vue and Axios.

On this post I'll be demonstrating how you can create a "REST" profile avatar uploader by utilizing VueJs and the axios http client using a Laravel back-end. On top of that I'll demonstrate how you can add a file upload progress to ameliorate the whole feature.

I will assume for the sake of this post that you are familiar with Laravel and Vue environments, if not head over to these links: Vue, Laravel.

Since we will be uploading files, profile images in this case, we first create a disk on Laravel that will hold these files in our project. Head to the filesystem config file and add the following code inside the disks array.

// config/filesystems.php

    'user_avatars' => [
        'driver' => 'local',
        'root' => storage_path('app/public/user-avatar'),
        'url' => env('APP_URL').'/storage/user-avatar',
        'visibility' => 'public',
    ],
Enter fullscreen mode Exit fullscreen mode

In the above code, we added a new disk called "user_avatars" whose root directory will be app/public/user-avatar, and it's public url will be "APP_URL/storage/user-avatar/IMAGE_FILE_NAME".

Create a new folder in the storage\app\public directory where the successfully uploaded avatar images will be stored and name it user-avatar.

Next, create the method that will take care of the files being uploaded. Wherever the controller is positioned, the method should have the following logic:

public function upload_user_photo(Request $request){
    // check if image has been received from form
    if($request->file('avatar')){
        // check if user has an existing avatar
        if($this->guard()->user()->avatar != NULL){
            // delete existing image file
            Storage::disk('user_avatars')->delete($this->guard()->user()->avatar);
        }

        // processing the uploaded image
        $avatar_name = $this->random_char_gen(20).'.'.$request->file('avatar')->getClientOriginalExtension();
        $avatar_path = $request->file('avatar')->storeAs('',$avatar_name, 'user_avatars');

        // Update user's avatar column on 'users' table
        $profile = User::find($request->user()->id);
        $profile->avatar = $avatar_path;

        if($profile->save()){
            return response()->json([
                'status'    =>  'success',
                'message'   =>  'Profile Photo Updated!',
                'avatar_url'=>  url('storage/user-avatar/'.$avatar_path)
            ]);
        }else{
            return response()->json([
                'status'    => 'failure',
                'message'   => 'failed to update profile photo!',
                'avatar_url'=> NULL
            ]);
        }

    }

    return response()->json([
        'status'    => 'failure',
        'message'   => 'No image file uploaded!',
        'avatar_url'=> NULL
    ]);
}
Enter fullscreen mode Exit fullscreen mode

A recap of the upload_user_photo() method: we first look if the image was sent in the form data, followed by deleting the existing user image from the user_avatars disk. Next, we generate a random name for the image using the helper method random_char_gen() . We then store the uploaded image inside the user_avatars disk and update the user's avatar column with the new public image url. Finally the method returns a respective json response.

Also, add a new post route on which the image will be posted through.

// routes/web.php
Route::post('upload_avatar', 'Controller@upload_user_photo');
Enter fullscreen mode Exit fullscreen mode

Tuning into the javascript side, start by installing axios to manage the http requests.

$ npm i axios
Enter fullscreen mode Exit fullscreen mode

Create the AvatarImageComponent.vue Vue component and place the following code in it.

<template>
    <div>
        <div class="avatar">
            <img :src="avatarImageUrl">
            <input type="file" ref="photo" accept="image/*" @change="updateAvatar()">
        </div>

        <div v-if="showUploadProgress">
            Uploading: {{ uploadPercent }} %
        </div>
    </div>
</template>
<script>
    import axios from 'axios'
    export default {
        name: "AvatarImageComponent",
        props: ['avatarUrl'];
        data() {
            return {
                uploadPercent: 0,
                avatarImageUrl: "",
                showUploadProgress: false,
                processingUpload: false
            }
        },
        mounted(){
            this.avatarImageUrl = this.avatarUrl
        },
        methods: {
            updateAvatar(){
                if(this.$refs.photo){
                    this.showUploadProgress = true
                    this.processingUpload = true
                    this.uploadPercent = 0
                    let formData = new FormData()
                    formData.append('avatar', this.$refs.photo)
                    axios.post('/upload_avatar', formData, {
                        onUploadProgress: (progressEvent) => {
                            this.uploadPercent = progressEvent.lengthComputable ? Math.round( (progressEvent.loaded * 100) / progressEvent.total ) : 0 ;
                        }
                    })
                    .then( (response) => {
                        this.avatarImageUrl = response.data.avatar_url
                        this.showUploadProgress = false
                        this.processingUpload = false
                        this.$emit('imageUrl', response.data.secure_url )
                    })
                    .catch( (error) => {
                        if(error.response){
                            console.log(error.message)
                        }else{
                            console.log(error)
                        }
                        this.showUploadProgress = false
                        this.processingUpload = false
                    })
                }
            }
        }
    }
</script>
Enter fullscreen mode Exit fullscreen mode

What happens in this component is that, when the onchange event is triggered in the file input, the selected image is wrapped in a formData object and posted to the server through the image upload route. We listen to axios's progress event and update the uploadPercent. On a successful image upload event, we update avatarImageUrl subsequently updating the avatar image being displayed, on failure, we log the error output without changing the current profile image being displayed.

Try implementing this and feel free to ask questions.

 
 

Buy Me A Coffee

Top comments (4)

Collapse
 
maximusblade profile image
Peter Sserwanga

Great peice of kit

Collapse
 
adearta11 profile image
manusia setengah mateng

since there is a table and model used, can you please add the model and database table to this tutorial, many thanks

Collapse
 
bawa_geek profile image
Lakh Bawa

Nice, thanks

Collapse
 
xinnks profile image
James Sinkala

You're welcome