Laravel

Panduan Lengkap File Upload di Laravel

06 Jun 2026 Administrator
Header Hero

Panduan Lengkap File Upload di Laravel

Sistem Unggah Berkas, Validasi Ketat, Manajemen Storage, dan Optimasi Keamanan API

Dalam pengembangan aplikasi web modern, fitur unggah berkas (*file upload*) merupakan komponen krusial yang hampir selalu ada—mulai dari pembaruan foto profil pengguna, pengunggahan dokumen lampiran, hingga manajemen aset media interaktif. Namun, menangani lalu lintas berkas dari sisi klien menyimpan tantangan tersendiri, terutama terkait performa server dan celah keamanan (*vulnerability execution*). Framework Laravel menyederhanakan kompleksitas ini dengan menyediakan abstraksi sistem berkas (*filesystem*) yang sangat tangguh, aman, dan modular di atas komponen *Flysystem*.

Melalui ekosistem Laravel, Anda dapat mengelola validasi tipe berkas secara ketat, memanipulasi nama berkas untuk menghindari tabrakan data, hingga memindahkan penyimpanan biner dengan sangat mudah antar *disk*. Baik Anda menggunakan penyimpanan lokal (*local storage*) pada VPS maupun memindahkannya ke penyedia *cloud storage* pihak ketiga seperti AWS S3 atau Google Cloud Storage, struktur kode dasar yang Anda gunakan tetap sama. Panduan ini akan membedah siklus pemrosesan berkas di Laravel dari tingkat dasar hingga praktik keamanan tingkat lanjut.

Aturan Utama HTML: Setiap kali Anda membuat form interaktif yang memuat input berkas, Anda wajib menyertakan atribut enctype="multipart/form-data". Tanpa atribut ini, browser tidak akan mengirimkan biner berkas ke server, dan request akan terbaca kosong.

1. Form Unggah Berkas Dasar

Berikut adalah implementasi struktur form Blade HTML untuk mengirimkan biner berkas ke *backend* controller:

@if(session('success'))
    <div style="background: #dcfce7; color: #15803d; padding: 15px; border-radius: 8px; margin-bottom: 20px;">
        {{ session('success') }}
    </div>
@endif

@if(session('error'))
    <div style="background: #fee2e2; color: #b91c1c; padding: 15px; border-radius: 8px; margin-bottom: 20px;">
        {{ session('error') }}
    </div>
@endif

<form action="{{ route('upload.store') }}" method="POST" enctype="multipart/form-data">
    @csrf
    <div style="margin-bottom: 20px;">
        <label style="display: block; font-weight: bold; margin-bottom: 8px;">Pilih Berkas</label>
        <input type="file" name="file" required>
    </div>

    <div style="margin-bottom: 20px;">
        <label style="display: block; font-weight: bold; margin-bottom: 8px;">Deskripsi</label>
        <input type="text" name="description" style="width: 100%; padding: 10px; border: 1px solid #cbd5e1; border-radius: 8px;">
    </div>

    <button type="submit" style="background: #4361ee; color: white; border: none; padding: 12px 25px; border-radius: 8px; font-weight: bold; cursor: pointer;">Upload File</button>
</form>
Langkah 2

Membuat Upload Controller

Gunakan perintah artisan untuk membuat skaffolding controller baru:

php artisan make:controller UploadController

Buka berkas app/Http/Controllers/UploadController.php dan implementasikan logika penanganan serta ekstraksi metadata berkas:

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;

class UploadController extends Controller
{
    public function create()
    {
        return view('upload.create');
    }

    public function store(Request $request)
    {
        // Validasi ukuran maksimal berkas (10240 KB = 10 MB)
        $request->validate([
            'file' => 'required|file|max:10240',
            'description' => 'nullable|string|max:255',
        ]);

        try {
            // Menyimpan berkas ke dalam direktori 'uploads' pada disk 'public'
            $path = $request->file('file')->store('uploads', 'public');

            // Ekstraksi Metadata Berkas
            $fileInfo = [
                'original_name' => $request->file('file')->getClientOriginalName(),
                'extension' => $request->file('file')->getClientOriginalExtension(),
                'mime_type' => $request->file('file')->getMimeType(),
                'size' => $request->file('file')->getSize(),
                'path' => $path,
                'description' => $request->description,
            ];

            return redirect()->back()->with('success', 'File uploaded successfully! Path: ' . $path);
        } catch (\Exception $e) {
            return redirect()->back()->with('error', 'File upload failed: ' . $e->getMessage());
        }
    }
}
Langkah 3

Validasi Berkas Tingkat Lanjut

Laravel menyediakan aturan validasi yang sangat ketat untuk membatasi tipe ekstensi, dimensi gambar, hingga ukuran muatan maksimum:

$request->validate([
    // Validasi dimensi gambar profil (dalam satuan piksel)
    'avatar' => [
        'required',
        'file',
        'image',
        'max:2048', // Maksimal 2MB
        'mimes:jpeg,png,jpg,gif,svg',
        'dimensions:min_width=100,min_height=100,max_width=1000,max_height=1000'
    ],
    // Validasi dokumen teks/PDF
    'documents.*' => [
        'required',
        'file',
        'max:5120',
        'mimes:pdf,doc,docx,txt'
    ],
    // Validasi Berkas Video
    'video' => [
        'nullable',
        'file',
        'max:51200',
        'mimes:mp4,mov,avi,wmv'
    ]
]);
Langkah 4

Unggah Banyak Berkas Sekaligus (Multiple Uploads)

Untuk mengunggah array berkas, gunakan notasi nama berformat array (name="files[]") pada HTML input dan lakukan urutan iterasi perulangan pada controller:

// Struktur Input Form HTML
<input type="file" name="files[]" multiple>

// Logika Penanganan Pada Sisi Controller
public function storeMultiple(Request $request)
{
    $request->validate([
        'files.*' => ['required', 'file', 'max:10240', 'mimes:jpg,jpeg,png,gif,pdf']
    ]);

    $paths = [];
    if($request->hasFile('files')) {
        foreach ($request->file('files') as $file) {
            $paths[] = $file->store('uploads', 'public');
        }
    }

    return redirect()->back()->with('success', count($paths) . ' files uploaded successfully!');
}

2. Konfigurasi Storage Disks

Daftarkan lokasi penyimpanan atau *drive* manajemen biner Anda di dalam berkas konfigurasi config/filesystems.php:

'disks' => [
    'local' => [
        'driver' => 'local',
        'root' => storage_path('app'),
    ],

    'public' => [
        'driver' => 'local',
        'root' => storage_path('app/public'),
        'url' => env('APP_URL').'/storage',
        'visibility' => 'public',
    ],

    's3' => [
        'driver' => 's3',
        'key' => env('AWS_ACCESS_KEY_ID'),
        'secret' => env('AWS_SECRET_ACCESS_KEY'),
        'region' => env('AWS_DEFAULT_REGION'),
        'bucket' => env('AWS_BUCKET'),
        'url' => env('AWS_URL'),
        'endpoint' => env('AWS_ENDPOINT'),
    ],
],

*Jangan lupa menjalankan perintah tautan simbolis php artisan storage:link agar folder public storage dapat diakses langsung via web URL browser.

3. Ragam Metode Manipulasi Berkas

Beberapa contoh metode eksekusi objek berkas yang sering digunakan dalam pengembangan aplikasi:

// Menyimpan di disk default bawaan
$path = $request->file('file')->store('uploads');

// Menyimpan dengan penamaan nama berkas kustom (Custom Filename)
$filename = 'avatar_' . time() . '.' . $request->file('file')->getClientOriginalExtension();
$path = $request->file('file')->storeAs('uploads', $filename, 'public');

// Mengambil URL absolut berkas untuk dilempar ke tag src HTML
$url = Storage::disk('public')->url($path);

// Menghapus berkas fisik dari ruang penyimpanan
Storage::disk('public')->delete($path);
Langkah 5

Integrasi dengan Database Relasional

Rekomendasi struktur skema migrasi tabel untuk menyimpan informasi berkas secara aman:

Schema::create('uploaded_files', function (Blueprint $table) {
    $table->id();
    $table->string('original_name');
    $table->string('filename');
    $table->string('path');
    $table->string('disk')->default('public');
    $table->string('mime_type');
    $table->integer('size')->unsigned();
    $table->text('description')->nullable();
    $table->timestamps();
});

Tambahkan kustom atribut akses (*Accessor*) pada model app/Models/UploadedFile.php untuk konversi ukuran data agar mudah dibaca manusia:

public function getHumanReadableSizeAttribute()
{
    if ($this->size < 1024) {
        return $this->size . ' B';
    } elseif ($this->size < 1024 * 1024) {
        return round($this->size / 1024, 2) . ' KB';
    } else {
        return round($this->size / (1024 * 1024), 2) . ' MB';
    }
}
Prosedur & Praktik Terbaik Keamanan File Upload

Membuka celah unggah berkas tanpa pengamanan ketat merupakan ancaman terbesar infeksi malware. Pastikan Anda mengimplementasikan aturan berikut di server produksi Anda:

  • Gunakan Nama Berkas Acak (Randomized Name): Hindari menyimpan berkas dengan nama asli bawaan klien. Gunakan UUID atau kombinasi string acak hash untuk menghindari eksploitasi jalur direktori (*path traversal*) atau penimpaan berkas (*file overwrites*).
  • Bypass Eksekusi Skrip PHP: Konfigurasikan web server Anda (Nginx/Apache) di folder penyimpanan uploads agar mematikan fungsi eksekusi file biner/skrip PHP. Hal ini mencegah peretas menjalankan skrip *backdoor* jika mereka berhasil menyelundupkan file biner berformat `.php`.
  • Validasi Real MIME-Type: Jangan hanya mempercayai ekstensi nama file yang dikirim oleh klien. Selalu gunakan pustaka bawaan Laravel untuk memverifikasi kontent biner asli berkas melalui pencocokan *Magic Numbers* pada biner berkas (MIME-Type validation).
  • Batasi Kuota Unggah (Rate Limiting): Pasang middleware pembatas laju request (*rate limiter*) pada rute API upload untuk menghindari serangan penimbunan memori penuh akibat bot otomatis yang membanjiri server dengan file ukuran besar secara terus-menerus.
v