Laravel Livewire Multiple File Upload Example

  1869 views   8 months ago Laravel

Larave Livewire is a full-stack web framework for Laravel that makes building dynamic interfaces. With using Laravel Livewire, you can build easy interface for CRUD operation or building dynamic component.

In this article, we will go through building multiple image upload.

After installing fresh Laravel project, first install Livewire using composer command.

composer require livewire/livewire

This will install Livewire. Short after installation, create a component for file upload.

php artisan make:livewire files.upload --inline

This will create component file at app/Http/Livewire/Files/Upload.php. Open the Upload class and add the methods in class.

<?php

namespace App\Http\Livewire\Files;

use Log;
use File;
use Storage;
use Exception;
use Livewire\Component;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Validator;

class FileUpload extends Component
{
    public $image;

    public $files = [];

    public $listeners = [
        "single_file_uploaded" => 'singleFileUploaded',
        "add_file" => 'addFile',
        "clear_files" => 'clearFiles',
        "clear_file" => 'clearFile',
    ];

    public $validation_errors = [];

    public function render()
    {
        return view('livewire.files.upload');
    }

    public function singleFileUploaded($file)
    {
        try {
            if($this->getFileInfo($file)["file_type"] == "image"){
                $this->image = $file;
            }else{
                session()->flash("error", "Uploaded file must be an image");
            }
        } catch (Exception $e) {
            
        }
    }

    public function addFile($file)
    {
        try {
            if($this->getFileInfo($file)["file_type"] == "image"){
                array_push($this->files, $file);
            }else{
                session()->flash("error", "Uploaded file must be an image");
            }
        } catch (Exception $e) {
            
        }
    }

    public function clearFile($index)
    {
        unset($this->files[$index]);
    }

    public function clearFiles()
    {
        $this->files = [];
    }

    public function getFileInfo($file)
    {
        $info = [
            "decoded_file" => null,
            "file_meta" => null,
            "file_mime_type" => null,
            "file_type" => null,
            "file_extension" => null,
        ];

        try {
            $info['decoded_file'] = base64_decode(substr($file, strpos($file, ',') + 1));
            $info['file_meta'] = explode(';', $file)[0];
            $info['file_mime_type'] = explode(':', $info['file_meta'])[1];
            $info['file_type'] = explode('/', $info['file_mime_type'])[0];
            $info['file_extension'] = explode('/', $info['file_mime_type'])[1];
        } catch(Exception $e) {

        }

        return $info;
    }

    public function uploadFiles()
    {
        try {

            $rules = [
                'image' => 'required',
                'files' => 'required',
            ];
    
            $messages = [
                "image.required" => "Image must be selected.",
                "files.required" => "Choose atleast one file.",
            ];

            $validator = Validator::make([
                "image" => $this->image,
                "files" => $this->files,
            ], $rules, $messages);

            $validator->after(function ($validator) {

                if($this->getFileInfo($validator->getData()["image"])["file_type"] != "image") {
                    $validator->errors()->add('image', 'File must be an image');   
                }
            });
            
            if($validator->fails()) {
                return $this->validation_errors = $validator->errors()->toArray();
            } else {
                // Single File Upload
                $file_data = $this->getFileInfo($this->image);
                $file_name = Str::random(10).'.'.$file_data['file_extension'];
                $result = Storage::disk('public')->put($file_name, $file_data['decoded_file']);
                
                // multiple files upload
                foreach($this->files as $value) {
                    $file_data = $this->getFileInfo($value);
                    $file_name = Str::random(10).'.'.$file_data['file_extension'];
                    $result = Storage::disk('public')->put($file_name, $file_data['decoded_file']);
                }
            }

            session()->flash("success", "Files uploaded successfully.");

            $this->image = null;
            $this->files = [];

        } catch (Exception $e) {
            session()->flash("error", "Files uploading error, please try again.");
        }
    }
}

Create a new route for file upload in routes/web.php.

<?php

Route::view('files', 'livewire.files.base');

Create a blade file base.blade.php. This file is main layout file.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Multiple file uploads</title>
    @livewireStyles
    @livewireScripts
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
</head>
<body>
    @livewire('files.upload')
    <script src="{{ asset('assets/js/jquery.min.js') }}"></script>
    <script src="{{ asset('assets/js/popper.min.js') }}"></script>
    <script src="{{ asset('assets/js/bootstrap.min.js') }}"></script>
</body>
</html>

Also create a blade file upload.blade.php.

<div class="container">
    <div class="wrapper">
        <form wire:submit.prevent="uploadFiles" method="post" enctype="multipart/form-data" >
            <div class="row header-container">
                <div class="col-md-12">
                    <h2 class="text-center">Laravel Livewire multiple file upload</h2>
                </div>
            </div>
            <div class="form-container">
                @if(session()->has('success'))
                    <div>
                        <div class="alert alert-success alert-dismissible fade show">
                            <button type="button" class="close" data-dismiss="alert">×</button>
                            {{ session('success') }}
                        </div>
                    </div>
                @endif
                @if(session()->has('error'))
                    <div>
                        <div class="alert alert-danger alert-dismissible fade show">
                            <button type="button" class="close" data-dismiss="alert">×</button>
                            {{ session('error') }}
                        </div>
                    </div>
                @endif
                <div class="row">
                    <div class="col-md-6">
                        <label for="">Image upload</label>
                        <input type="file" class="form-control" wire:change="$emit('single_file_choosed')" ><br>
                        <span>Only .jpg, .png, .gif files are allowed.</span>
                        @if(!empty($validation_errors["image"]))
                            @foreach($validation_errors["image"] as $k => $v)
                                <label for="" class="error" >{{ $v }}</label>
                            @endforeach
                        @endif
                    </div>
                    <div class="col-md-12">
                        @if($image)
                            <br>
                            <img src="{{ $image }}" alt="" style="height: 150px;" class="img-thumbnail" >
                        @endif
                    </div>
                </div>
                <br>
                <div class="row">
                    <div class="col-md-6">
                        <label for="">Multiple image upload</label>
                        <input type="file"  class="form-control"  wire:change="$emit('multiple_file_choosed')" multiple> 
                        @if(!empty($files)) <button type="button" wire:click="$emit('confirm_remove_files')"  class="btn btn-danger">Clear Files</button> @endif <br>
                        @if(!empty($validation_errors["files"]))
                            @foreach($validation_errors["files"] as $k => $v)
                                <label for="" class="error" >{{ $v }}</label>
                            @endforeach
                        @endif
                    </div>
                    <div class="col-md-12">
                        @if(!empty($files))
                            <div class="row">
                                @foreach($files as $k => $v)
                                    <div class="col-md-3">
                                        <img src="{{ $v }}" alt="" style="height: 100px;object-fit:contain;" class="img-thumbnail" >
                                        <br>
                                        <button type="button" wire:click="$emit('confirm_remove_file', {{ $k }})"  class="btn btn-danger btn-sm" >Clear Files</button>
                                    </div>
                                @endforeach
                            </div>
                            <br>
                        @endif
                    </div>
                </div>
                <div class="row">
                    <div class="col-md-3">
                        <br>
                        <input type="submit" class="btn btn-success" value="Upload Now">
                    </div>
                </div>
            </div>
        </form>

    </div>
    <script>
        window.livewire.on('single_file_choosed', function(){
            try {
                let file = event.target.files[0];
                if(file){
                    let reader = new FileReader();

                    reader.onloadend = () => {
                        window.livewire.emit('single_file_uploaded', reader.result);
                    }
                    reader.readAsDataURL(file);
                }
            } catch (error) {
                console.log(error);
            }
        });
        window.livewire.on('multiple_file_choosed', function(){
            try {
                let files = event.target.files;
                let read_files = [];
                if(files.length>0){
                    for(let index in files){
                        if(typeof files[index] == "object"){
                            let reader = new FileReader();
                            reader.onloadend = () => {
                                window.livewire.emit('add_file', reader.result);
                            }
                            reader.readAsDataURL(files[index]);
                        }
                    }
                }
            } catch (error) {
                console.log(error);
            }

        });

        window.livewire.on('confirm_remove_files', function(){
            let cfn = confirm("Confirm to remove all files");
            if(cfn){
                return  window.livewire.emit('clear_files');
            }
            return false;
        });
        window.livewire.on('confirm_remove_file', function(index){
            let cfn = confirm("Confirm to remove this file");
            if(cfn){
                return  window.livewire.emit('clear_file', index);
            }
            return false;
        });
    </script>
</div>

Now everything is ready, we will need to run Laravel application and test it. Run the artisan command to start application server.

php artisan serve

And open below URL on your browser:

http://localhost:8000/files

Conclusion

In this article, you have created multiple file upload example in Laravel Livewire. I hope you liked this article and help in your work. Thank you for giving time in reading article.

Author : Harsukh Makwana
Harsukh Makwana

Hi, My name is Harsukh Makwana. i have been work with many programming language like php, python, javascript, node, react, anguler, etc.. since last 5 year. if you have any issue or want me hire then contact me on [email protected]