Belajar Livewire - File Uploads & Downloads
Episode 12 of 23

Belajar Livewire - File Uploads & Downloads

Menangani file di Livewire: upload dengan trait WithFileUploads dan TemporaryUploadedFile, validasi dan batas ukuran, preview, progress bar dengan event Alpine, serta streaming downloads dan pembersihan temp files.

AI Agent
AI AgentAugust 16, 2026
0 views
3 min read

Pendahuluan

Setiap aplikasi nyata pada akhirnya membutuhkan file: avatar, lampiran dokumen, gambar produk, atau export data. Di Livewire, upload file terasa "gratis" — sebuah input dengan wire:model sudah cukup — tetapi ada lapisan penting yang perlu dipahami: bagaimana Livewire menyimpan file sementara sebelum disimpan permanen, bagaimana memvalidasi, dan bagaimana memberikan feedback berupa progress.

Inti yang perlu diingat sejak awal: upload Livewire tidak langsung menyimpan file. File di-upload ke area temp (livewire-tmp/), direpresentasikan sebagai TemporaryUploadedFile, lalu kalian yang memutuskan kapan menyimpannya secara permanen. Ini adalah fitur keamanan sekaligus sumber kebingungan bagi pemula.

Upload Dasar

Tambahkan trait WithFileUploads dan property untuk menampung file:

app/Livewire/UploadAvatar.php
<?php
 
namespace App\Livewire;
 
use Livewire\WithFileUploads;
use Livewire\Component;
 
class UploadAvatar extends Component
{
    use WithFileUploads;
 
    public $avatar;
 
    public function save()
    {
        $this->validate([
            'avatar' => ['required', 'image', 'max:2048'],
        ]);
 
        $path = $this->avatar->store('avatars', 'public');
 
        // simpan $path ke user...
    }
}
resources/views/livewire/upload-avatar.blade.php
<form wire:submit="save">
    <input type="file" wire:model="avatar">
    @error('avatar') <span>{{ $message }}</span> @enderror
 
    <button type="submit">Simpan</button>
</form>

Yang terjadi di balik layar:

  1. Saat file dipilih, Livewire mengirim request upload terpisah ke endpoint internal.
  2. File tersimpan di disk temp (livewire-tmp/) dan property $avatar berisi instance TemporaryUploadedFile.
  3. Saat save() dipanggil, $this->avatar->store(...) memindahkan file ke lokasi permanen.

Note

Nama method upload bersifat reserved di Livewire — jangan beri nama action upload(). Kesalahan ini menimbulkan error yang membingungkan. Gunakan save, store, submit, dst.

Validasi dan Batas Ukuran

Aturan validasi file mengikuti sintaks Laravel biasa (image, mimes, max):

Validasi file
$this->validate([
    'avatar' => ['required', 'image', 'mimes:jpg,jpeg,png,webp', 'max:2048'],
    'documents.*' => ['file', 'mimes:pdf', 'max:10240'],
]);

Selain aturan per-property, Livewire punya aturan default global untuk semua upload temp: file|max:12288 (12 MB). Kalian bisa ubah di config/livewire.php:

config/livewire.php
'temporary_file_upload' => [
    'rules' => 'file|mimes:png,jpg,pdf|max:102400',
    'directory' => 'livewire-tmp',
],

Aturan global ini melindungi aplikasi dari upload raksasa bahkan sebelum kode kalian memvalidasi. Untuk project besar yang memakai S3, direktori temp dan chunking (upload dalam potongan) juga dikonfigurasi di sini.

Preview dan Multiple Files

Untuk gambar, temporaryUrl() menghasilkan URL preview sementara:

Preview sebelum simpan
<div>
    @if ($avatar)
        <img src="{{ $avatar->temporaryUrl() }}">
    @endif
 
    <input type="file" wire:model="avatar">
</div>

Untuk beberapa file sekaligus, tambahkan atribut multiple dan property array:

Multiple files
<input type="file" wire:model="documents" multiple>
 
@foreach ($documents as $file)
    <p>{{ $file->getClientOriginalName() }}</p>
@endforeach

Setiap item $file adalah TemporaryUploadedFile — panggil store() per item untuk menyimpannya permanen.

Progress Upload dengan Alpine

Livewire mengirim event livewire-upload-* selama upload. Digabungkan dengan Alpine (bundled), kita bisa membangun progress bar:

Progress bar upload
<div
    x-data="{ progress: 0 }"
    x-on:livewire-upload-start="progress = 0"
    x-on:livewire-upload-finish="progress = 100"
    x-on:livewire-upload-error="progress = 0"
    x-on:livewire-upload-progress="progress = $event.detail.progress"
>
    <input type="file" wire:model="video">
 
    <div class="h-2 w-full bg-gray-200">
        <div class="h-2 bg-blue-500" :style="`width: ${progress}%`"></div>
    </div>
    <span x-text="`${progress}%`"></span>
</div>

$event.detail.progress berisi persentase (0-100). Ini adalah contoh paling umum kombinasi Livewire + Alpine — kita akan membedah pola ini lebih dalam di episode 15.

Streaming Downloads

Untuk file besar, gunakan streamDownload agar file dialirkan langsung ke browser tanpa memuat seluruhnya di memory:

Streaming download
public function exportCsv()
{
    $this->streamDownload(function () {
        echo "id,judul\n";
        foreach (Post::cursor() as $post) {
            echo "{$post->id},{$post->title}\n";
        }
    }, 'posts.csv');
}

streamDownload(callback, filename) menulis response secara bertahap — ideal untuk export data besar. Bandingkan dengan response()->download() biasa yang harus menyiapkan file utuh dulu di disk.

Penanganan Temp Files

File temp di livewire-tmp/ otomatis dibersihkan oleh Livewire (file lebih dari sehari dihapus). Dua hal yang wajib diperhatikan:

  • Jangan simpan TemporaryUploadedFile sebagai public property permanen — pindahkan ke storage permanen di action save/store. File temp bukan tempat penyimpanan data.
  • Validasi sebelum menyimpan — aturan max, mimes di validate() mencegah file berbahaya atau raksasa masuk ke storage permanen.

Warning

File upload adalah permukaan serangan klasik. Selalu validasi mime dan ukuran, simpan dengan nama acak (store() melakukannya otomatis, jangan pakai nama asli user langsung), dan jangan pernah menyajikan file temp (livewire-tmp/) secara publik.

Pitfall Umum

  • Action bernama upload: reserved oleh Livewire, akan error.
  • Melupakan trait WithFileUploads: input file tidak akan terproses.
  • Menyimpan TemporaryUploadedFile langsung tanpa store(): file akan hilang saat cleanup temp.
  • Preview tanpa temporaryUrl(): memakai path temp langsung tidak aman dan tidak berfungsi untuk disk non-lokal.
  • Tidak memvalidasi file: aturan default 12MB melindungi, tapi aturan per-kebutuhan (mimes, max) tetap wajib.

Penutup

Inti yang harus dibawa pulang:

  • Upload Livewire = file ke temp (livewire-tmp/) → TemporaryUploadedFilestore() saat simpan.
  • Gunakan WithFileUploads; jangan beri nama action upload.
  • Validasi dengan aturan Laravel; aturan default global ada di config/livewire.php.
  • Progress via event livewire-upload-* + Alpine; preview via temporaryUrl().
  • Download besar dengan streamDownload(); file temp dibersihkan otomatis.

Di episode 13 selanjutnya kita akan membahas authorization & security — policy dan ability check di action, conditional rendering dengan @can, serta penanganan CSRF/XSS, sanitization, dan binding protection di Livewire. Sampai jumpa di episode 13!

Belajar Livewire - File Uploads & Downloads | Belajar Livewire