Search code examples
laravellaravel-livewireimage-compression

How to compress the files on frontend in Livewire


I am using browser-image-compression library to compress the images before uploading. Now it gives the compressed Image and the compressed image is also stored in the storage folder.

But I want to display the uploaded attachment as soon as the image is uploaded. I tried to dd($this->uploaded_cost_attachments) in component and it gives response there but when I try to <div>@dump($uploaded_cost_attachments)</div> It is always empty.

**
Livewire Component:**

public $cost_attachments = [];
public $uploaded_cost_attachments = [];
public function updatedCostAttachments()
 {
    $this->validate([
      'cost_attachments.*' => 'file|mimes:jpg,png,pdf,xls,xlsx',
    ]);

    if ($this->cost_attachments) {
      foreach ($this->cost_attachments as $file) {
                
         $filePath = $file->store('cost_attachments', 'public');

         $this->uploaded_cost_attachments[] = [
             'file' => $filePath,
              'id' => null,
         ];
     }
   }
 }

Blade.php:

<div class="mt-2">
   <div class="relative">
      <input onchange="handleImageUpload(event);" type="file" multiple id="file-input" />
                                                
    </div>
</div>

<div class="mt-2">
    @foreach($uploaded_cost_attachments as $key => $file)
        <span class="inline-flex items-center gap-x-1.5 rounded-md px-2 py-1 text-xs font-medium 
            text-gray-900 ring-1 ring-inset ring-gray-200">
        <svg wire:click="removeCostAttachment({{ $key }})" xmlns="http://www.w3.org/2000/svg" 
             viewBox="0 0 20 20" fill="currentColor" class="w-5 h-5 cursor-pointer">
             <path d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 
                    11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 
                    8.94 6.28 5.22z" />
        </svg>
        <a target="_blank" href="{{ asset('storage/'.$file['file']) }}">
            ...{{ substr($file['file'], -10) }}
        </a>
        </span>
    @endforeach
</div>


<script>
    // Image compression
        async function handleImageUpload(event) {

            const files = event.target.files;

            const options = {
                maxSizeMB: 2,
                maxWidthOrHeight: 1920,
                useWebWorker: true,
            }
            for (const file of files) {
                const fileType = file.type;

                if (fileType === "application/pdf") {
                    // Handle PDF upload without compression
                    console.log('PDF File:', file.name);

                    // Upload the PDF file directly without compression
                    @this.upload('cost_attachments', file, (uploadedFilename) => {
                        console.log('PDF uploaded:', uploadedFilename);
                    }, (error) => {
                        console.error('Upload error:', error);
                    }, (event) => {
                        console.log('Upload progress:', event.detail.progress);
                    });

                } else if (fileType.startsWith('image/')) {
                    try {
                        // Compress images
                        const compressedFile = await imageCompression(file, options);
                        console.log('Compressed Image:', compressedFile.name);
                        console.log(`Compressed size: ${compressedFile.size / 1024 / 1024} MB`);

                        // Upload the compressed image
                        @this.upload('cost_attachments', compressedFile, (uploadedFilename) => {
                            console.log('Image uploaded:', uploadedFilename);
                        }, (error) => {
                            console.error('Upload error:', error);
                        }, (event) => {
                            console.log('Upload progress:', event.detail.progress);
                        });

                    } catch (error) {
                        console.error('Image compression error:', error);
                    }
                } else {
                    console.error('Unsupported file type:', fileType);
                }
            }


        }
</script>

I am using the input without wire:model, following this answer on github.


Solution

  • Finally I solved it by emiting an event from component when the files are uploaded and then when I listen the event in blade I update the uploaded_cost_attachments array with updated array I received in event.

    public function costAttachments()
        {
            $this->validate([
                'cost_attachments.*' => 'file|mimes:jpg,png,pdf,xls,xlsx',
            ]);
    
            if ($this->cost_attachments) {
                foreach ($this->cost_attachments as $file) {
                    $filePath = $file->store('cost_attachments', 'public');
                    $this->uploaded_cost_attachments[] = [
                        'file' => $filePath,
                        'id' => null,
                    ];
                }
                $this->emit('fileUploaded', $this->uploaded_cost_attachments);
                $this->cost_attachments = [];
            }
        }
    

    and in blade:

    document.addEventListener('livewire:load', function() {
        Livewire.on('fileUploaded', function(array) {
            @this.set('uploaded_cost_attachments', array);
        });
    });
    

    by doing this now the array uploaded_cost_attachments get updated and display the attachments.