Search code examples
vue.jslaravel-9

Trying to upload image files in a laravel, Inertia, vue application


I build a Laravel SPA for a client. The client wanted a portfolio website that would allow him to upload images as well as data pertaining to that image. So I build a controller:

public function store(Request $request): Response
{
    PaintingService::createPaintingService($request);

    //This will return to the main gallery
    return self::returningToGallery();
}

And I also created a service for this method:

    public static function createPaintingService($request): void
{
    if ($request->hasFile('image')) {
        $fileName = time() . '.' . $request->image->extension();

        $request->image->move(public_path('storage'), $fileName);

        Painting::create([
            'title' => $request->title,
            'size' => $request->size,
            'medium' => $request->medium,
            'location' => $request->location,
            'frame_status' => $request->frame_status,
            'status' => $request->status,
            'notes' => $request->notes,
            'category' => $request->category,
            'image' => $fileName
        ]);
    }

}

The form written is vue:

<template>
<backend-layout>
    <h2 class = "text-center text-4xl mb-10 tracking-wider">Create a New Painting</h2>
    <div class = "flex justify-center m-8">
        <form @submit.prevent="submit" enctype="multipart/form-data" method="post">
            <div class="flex flex-wrap -mx-3 mb-6">
                <div class = "w-full md:w-1/2 px-3 mb-6 md:mb-0">
                    <label class = "block uppercase tracking-wide text-gray-700 text-xs font-light mb-1"
                           for = "project-title">
                        Painting Title
                    </label>
                    <input
                        id = "project-title"
                        v-model = "form.title"
                        class = " appearance-none block w-full bg-gray-200 text-gray-700 border border-red-500 rounded py-3 px-4 mb-3 leading-tight focus:outline-none focus:bg-white-500"
                        placeholder = "Project Title" required
                        type = "text">
                </div>
                <div class = "w-full md:w-1/2 px-3 mb-6 md:mb-0">
                    <label class = "block uppercase tracking-wide text-gray-700 text-xs font-light mb-1"
                           for = "project-category">
                        Painting Category
                    </label>
                    <input
                        id = "project-category"
                        v-model = "form.category"
                        class = " appearance-none block w-full bg-gray-200 text-gray-700 border border-red-500 rounded py-3 px-4 mb-3 leading-tight focus:outline-none focus:bg-white-500"
                        placeholder = "Category" required
                        type = "text">
                </div>
                <div class = "w-full md:w-1/2 px-3 mb-6 md:mb-0">
                    <label class = "block uppercase tracking-wide text-gray-700 text-xs font-light mb-1"
                           for = "painting-size">
                        Painting Size
                    </label>
                    <input
                        id = "painting-size"
                        v-model = "form.size"
                        class = " appearance-none block w-full bg-gray-200 text-gray-700 border border-red-500 rounded py-3 px-4 mb-3 leading-tight focus:outline-none focus:bg-white-500"
                        placeholder = "Painting Size" required
                        type = "text">
                </div>
                <div class = "w-full md:w-1/2 px-3 mb-6 md:mb-0">
                    <label class = "block uppercase tracking-wide text-gray-700 text-xs font-light mb-1"
                           for = "painting-medium">
                        Painting Medium
                    </label>
                    <input
                        id = "painting-medium"
                        v-model = "form.medium"
                        class = " appearance-none block w-full bg-gray-200 text-gray-700 border border-red-500 rounded py-3 px-4 mb-3 leading-tight focus:outline-none focus:bg-white-500"
                        placeholder = "Painting Medium" required
                        type = "text">
                </div>
                <div class = "w-full md:w-1/2 px-3 mb-6 md:mb-0">
                    <label class = "block uppercase tracking-wide text-gray-700 text-xs font-light mb-1"
                           for = "painting-location">
                        Painting Location
                    </label>
                    <input
                        id = "painting-location"
                        v-model = "form.location"
                        class = " appearance-none block w-full bg-gray-200 text-gray-700 border border-red-500 rounded py-3 px-4 mb-3 leading-tight focus:outline-none focus:bg-white-500"
                        placeholder = "Painting Location"
                        required type = "text">
                </div>
                <div class = "w-full md:w-1/2 px-3 mb-6 md:mb-0">
                    <label class = "block uppercase tracking-wide text-gray-700 text-xs font-light mb-1"
                           for = "painting-frame">
                        Painting Frame
                    </label>
                    <input
                        id = "painting-frame"
                        v-model = "form.frame_status"
                        class = " appearance-none block w-full bg-gray-200 text-gray-700 border border-red-500 rounded py-3 px-4 mb-3 leading-tight focus:outline-none focus:bg-white-500"
                        placeholder = "Painting Frame"
                        required type = "text">
                </div>
                <div class = "w-full md:w-1/2 px-3 mb-6 md:mb-0">
                    <label class = "block uppercase tracking-wide text-gray-700 text-xs font-light mb-1"
                           for = "painting-status">
                        Painting Status
                    </label>
                    <select
                        id = "skill-percentage"
                        v-model = "form.status"
                        class = "appearance-none block w-full bg-gray-200 text-gray-700 border border-red-500 rounded py-3 px-4 mb-3 leading-tight focus:outline-none focus:bg-white-500"
                        name = "skill_percentage" required>
                        <option value = "available">Available</option>
                        <option value = "unavailable">Not Available</option>
                        >
                    </select>
                </div>
                <div class="w-full md:w-1/2 px-3 mb-6 md:mb-0">
                    <label class="block uppercase tracking-wide text-gray-700 text-xs font-light mb-1"
                           for="painting-image">
                        Painting Image
                    </label>
                    <input
                        id = "painting-image"
                        name = "image"
                        class = " appearance-none block w-full bg-gray-200 text-gray-700 border border-red-500 rounded py-3 px-4 mb-3 leading-tight focus:outline-none focus:bg-white-500"
                        placeholder = "Painting Image"
                        type = "file" @change = "previewImage"
                        required
                        @input = "form.image = $event.target.files[0] || $event.dataTransfer.files">
                </div>
                <div class = "w-full md:w-1/2 px-3 mb-6 md:mb-0">
                    <label class = "block uppercase tracking-wide text-gray-700 text-xs font-light mb-1"
                           for = "painting-notes">
                        Notes
                    </label>
                    <textarea
                        id = "painting-notes"
                        v-model = "form.notes"
                        class = "appearance-none block w-full bg-gray-200 text-gray-700 border border-red-500 rounded py-3 px-4 mb-3 leading-tight focus:outline-none focus:bg-white-500"
                        cols = "30" name = "notes" placeholder = "Painting Notes"
                        required rows = "10"></textarea>
                </div>
                <div class="w-full md:w-1/2 px-3 mt-5">
                    <img :src = "url" :alt = "url">
                </div>
            </div>
            <button class = "inline-block m-5" type = "submit">Submit</button>
            <Link href="/paintings/" class="inline-block m-5">Back</Link>
        </form>
    </div>
</backend-layout>

The script of that vue file:

export default {
name: "create",
components: {
    "backend-layout": backendLayout,
    Link
},
setup() {
    const form = useForm({
        title: null,
        size: null,
        medium: null,
        location: null,
        frame_status: null,
        status: null,
        notes: null,
        category: null,
        image: null,
    });

    function submit(){
        form.post(route('paintings.store'),{
            forceFormData: true,
        });
       // Inertia.post(route('paintings.store'), form);
    }

    return { form, submit }
},
data(){
    return{
        url: null
    }
},
methods:{
    previewImage(e){
        const imageURL = e.target.files[0];
        this.url = URL.createObjectURL(imageURL);
    }
},

I was able to get some images. When I conduct a dd test on images that work. enter image description here

But there are images that won't upload: enter image description here

I am not sure why.


Solution

  • I discovered after posting this that the memeType is not “image/jpeg” or some other type of image. Instead, it was a generic type “application/octet-stream”. Once I reformatted the image by renaming the image then saving it, I was able to change it the desired memeType.