How to make file upload work?

Hello,

I can’t figure out the best way (or any way :slight_smile: ) to get file from input.

In most cases it just looks like I couldn’t get the way to uploaded files.

Any help would be appreciated.

Hi Klera,

Could you please share your blade and php code?
Showing what you have would help us to see what you have so far and therefore we would have a better chance to help you.

Sure, thank you.

image-editor.blade.php

<div class="flex">
    <div class="flex-1">
        <img src="{{$field['text']}}">
        <div>
            <h2>Change image:</h2>
            <form enctype="multipart/form-data" wire:submit.prevent="uploadImage">
                <input type="file" name="image" wire:model="image">
                <button type="submit">Update</button>
            </form>
        </div>
    </div>
    <button wire:click.prevent="update" class="bg-blue-600 text-white py-2 px-6">
        Save
    </button>
</div>

ImageEditor.php

class ImageEditor extends Component
{
    public $field;
    public $section;
    public $text;
    public $image;

    public function mount($field, $section)
    {
        $this->field = $field;
        $this->text = $field['text'];
        $this->section = $section;
    }

    public function uploadImage()
    {
        dd(request()->file('image'));
    }

    public function update()
    {
        // This is giving me null, because here I'm not sure how to access file inside the input field
        dd($this->image);
    }  

    public function render()
    {
        return view('livewire.image-editor');
    }
}

Thanks for the prompt response.

This is how I see this problem.

The user clicks the “Chose file” button and selects the image (wire:model=“image” will add the temp path to the file). When the user clicks on the “Save” button you should process the image by saving it to the storage.
Now! When you click on the update button, there is no selected image available. I know you trying to access the “$this->image” public variable, but php doesn’t saves the data into database/cache, etc. It works on the fly… e.g. you send something and processing it on the server and once php passed the values back it forget about all data.

If you want to update the image, then I guess you have an image showing for the user. Behind that image, the image ID, or user ID is coupled. When you want to update, you select a new image and then press the update button. Once the new image along with the ID passed to the server, you then can look for the model using the ID and process the image (save it to the storage and reference it on the model…)

I hope I understood your problem right and I hope that this was helpful to you.
If not, please feel free to PM me. I will try my best to help!

Thank you very much.

I’m a little confused here, in a way how should I handle this - wire:model=“image” gives me temp path to the file as a string. I’m not sure how to convert that string and store the file.

This is from one of my normal Laravel Controller.
It will need some changes, but in the sake of your learning curve I didn’t want to give you a fully working snippet.

use Image; // Add this to the top of the class ---> see the installation link below

// You could call this method from your update button
public function profileAvatarChange(Request $request)
    {
        if ($request->hasFile('avatar')) { 															// This is to check if the request have 'avatar'
            $avatar = $request->file('avatar'); 													// Assigning the avatar file to $avatar variable
            $filename = 'useravatars/' . time() . '.' . $avatar->getClientOriginalExtension();		// Creating the unique filename
            Image::make($avatar)->save('storage/' . $filename);										// Saving the file to the storage
            $user = auth()->user();																	// Getting the current user
            $user->avatar = $filename;																// Adding the filename to the user model
            $user->save();																			// Saving the user model
        }

        return redirect()->route('users.profile', $user->id);
    }

The image facade is the Intervention Image… Here is the link

<form method="POST" enctype="multipart/form-data" wire:submit.prevent="uploadImage">
@csrf
<input type="file" name="image" wire:model="image">
<input type="text" name="bla" wire:model="bla" value="test">
<button type="submit">Update</button>

Thank you for your effort once again.

I have done the code you mentioned earlier in Laravel and I understand it and I have also done this with Vue&axios before. However, when I want to use it through Livewire if I would dump these two:

$request->input('bla'); // returns null
$request->file('image'); // returns null

So I’m not sure about the best way to implement this in Livewire - if it is through a form then I get a new full page reload.

Okay, so I’ve had some play around with this and I tried to replicate your problem.
I have to apologise as I can feel your pain now.

There is a package here which is by the way a very nice piece of work. Thanks for @kdion4891
I tried to use Kevin’s package, which works great until I try to use it for file uploads. I’m not sure if I do something wrong or there is a bug in the package, but I’ve raised a new ticket on his github page. See issue ticket here.

Hang in there buddy! We will get this sorted!
I’m just as keen to get to the bottom of this problem as you are!

1 Like

Ok, so it ain’t just me. Thank you for your help, I’ll write a solution here if I find it.

wire:model isn’t going to work on a file input(currently anyway by looking at the input value), because the only thing it sees is what the browser says is the local file location, which in most cases the browser is going to say something along the lines of C:\fakepath\filename.ext. That’s a browser security feature, has nothing to do with laravel or livewire. The browser knows where it is, but it isn’t going to tell the site or allow it to have access to that information. The file contents have to be sent through a request which will just have the file name and not the full local path.

I was curious about this, so I started playing with it too.

I don’t think it’s possible yet in livewire. In a regular form request with laravel, the files get added to a FileBag object and then gets stored in a temp folder.

Here’s what it looks like and the information you have to work with:
image
Every way I have tried to send it through livewire, the filebag never gets it:
image
The only thing in the livewire ajax request that references the file at all is if you bind a property, but again this is all you will get
image

I’m sure there’s some behind the scenes functionality that can be added to make this work, but that is beyond what this guy knows how to do.

For a work around, I was just going to suggest doing it “the old school” way through an ajax request of your own, and handle everything else through livewire. However, I found someone else figured out a better workaround already which is a combination of old school ajax and livewire. https://github.com/livewire/livewire/issues/106 I think I should have started there first, but oh well. At least we have some insight into why now.

1 Like

Thanks Dale,

I was looking into a bit, but due to being crazy busy in the past few weeks didn’t have much time to play around with it.
I also checked the bag couple of weeks ago and had similar thoughts, but never actually went beyond and get the answer.
I was doing it the good old school way, which isn’t that bad.
Thank you for the link!

I’m not sure about the ‘best’ way, but this certainly is a solution. It solves uploads without leaving the component.

Edit: For some reason my comment got flagged. Maybe because of the github link. Removed that.
Please check out github issue 937 my workaround for handling file inputs.

I am sharing my solution over here,
In my Component:

<script>
    window.livewire.on('fileChoosen', () => {
        let inputField = document.getElementById('logo')
        let file = inputField.files[0]
        let reader = new FileReader();
        reader.onloadend = () => {
            window.livewire.emit('fileUpload', reader.result)
        }
        reader.readAsDataURL(file);
    })
</script>

After that you need to go to the class and do the following:

protected $listeners = [
        'fileUpload'     => 'update',
    ];
public function update($imageData)
    {
            $date = Carbon::now()->toDateString();
            ## MAKE IMAGE UNIQUE NAME ##
            $imageName = 'app-logo-' . $date . '-' . uniqid() . '.' . explode('/', explode(':', substr($imageData, 0, strpos($imageData, ';')))[1])[1];
            
            $logo = Image::make($imageData)->save($imageData->explode('/', explode(':', substr($imageData, 0, strpos($imageData, ';')))[1])[1]);
            ## PUT IMAGE IN USERS DIRECTORY ##
            Storage::disk('public')->put('users/'.$imageName,$logo);
}

The file which would be sent to your class would be in base64, to get the file extension I used the explode function. Hope it helps you.

1 Like

File upload is answered in this thread