Cara Upload File Di Laravel Ke Storage Public Dengan Aman

| | | Laravel
Cara Upload File Di Laravel Ke Storage Public Dengan Aman

Berikut ini cara upload file di Laravel ke folder storage public dan menyimpan path-nya di database. tujuannya untuk menampilkan gambar dari database dan melakukan aksi simpan, update, dan delete. saya pakai Laravel 11 dengan pendekatan Storage, karena lebih aman dan fleksibel dibanding memindahkan file langsung ke public.

Install Laravel di localhost

di tutorial ini saya pakai Laravel versi terbaru (11.x). langsung saja jalankan composer:

composer create-project laravel/laravel upload-file

bagi yang belum paham instalasi laravel bisa baca Cara Install Laravel di Windows atau Cara install PHP di Windows dan Berjalan di CMD (Command Prompt)

Menyiapkan storage public

sebelum upload, kita siapkan dulu symlink storage supaya file yang disimpan di storage/app/public bisa diakses publik:

php artisan storage:link

command ini membuat folder public/storage yang menunjuk ke storage/app/public. jadi file yang kita simpan di storage public bisa diakses lewat URL /storage/....

Membuat Controller untuk upload file

buat controller app/Http/Controllers/ImageUploadController.php yang melakukan validasi dan penyimpanan file:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Str;

class ImageUploadController extends Controller
{
    public function imageUpload()
    {
        return view('imageUpload');
    }

    public function imageUploadPost(Request $request)
    {
        $request->validate([
            'image' => 'required|image|mimes:jpeg,png,jpg,gif,svg|max:2048',
        ]);

        // generate nama file unik biar tidak bentrok
        $imageName = Str::uuid() . '.' . $request->image->extension();

        // simpan ke storage/app/public/images
        $path = $request->image->storeAs('images', $imageName, 'public');

        return back()
            ->with('success', 'Berhasil melakukan upload file di laravel')
            ->with('image', $path);
    }
}

Penjelasan :

  • $request->validate([...]) : validasi file. image memastikan file adalah gambar, mimes membatasi ekstensi, max:2048 membatasi ukuran maksimal 2 MB (dalam kilobyte)
  • Str::uuid() : generate nama unik supaya dua user yang upload file sama tidak saling menimpa
  • $request->image->storeAs('images', $imageName, 'public') : menyimpan file ke folder storage/app/public/images dengan disk public, method ini yang menggantikan move() ke public_path. hasilnya path relatif seperti images/xxx.webp

Membuat route di Laravel

lanjut buat route di routes/web.php:

<?php

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\ImageUploadController;

Route::get('image-upload', [ImageUploadController::class, 'imageUpload'])->name('image.upload');
Route::post('image-upload', [ImageUploadController::class, 'imageUploadPost'])->name('image.upload.post');

Penjelasan :

  • Route::get('image-upload', ...) : endpoint URL image-upload dengan method GET, menampilkan form
  • Route::post('image-upload', ...) : endpoint yang sama tapi method POST, menerima file dari form
  • ->name('image.upload') : penamaan route supaya bisa dipanggil lewat route('image.upload') di blade

Membuat view blade HTML form upload

buat file resources/views/imageUpload.blade.php:

<!DOCTYPE html>
<html>
  <head>
    <title>Laravel File Upload</title>
    <link
      rel="stylesheet"
      href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"
    />
  </head>
  <body>
    <div class="container mt-5">
      <div class="card">
        <div class="card-header"><h2>Laravel File Upload</h2></div>
        <div class="card-body">
          @if (session('success'))
          <div class="alert alert-success">
            <strong>{{ session('success') }}</strong>
          </div>
          @if (session('image'))
          <img src="{{ asset('storage/' . session('image')) }}" class="img-fluid mt-2" style="max-width: 300px" />
          @endif
          @endif @if ($errors->any())
          <div class="alert alert-danger">
            <strong>Whoops!</strong>, Ada masalah ketika melakukan input data.
            <ul>
              @foreach ($errors->all() as $error)
              <li>{{ $error }}</li>
              @endforeach
            </ul>
          </div>
          @endif
          <form action="{{ route('image.upload.post') }}" method="POST" enctype="multipart/form-data">
            @csrf
            <div class="mb-3">
              <input type="file" name="image" class="form-control" />
            </div>
            <button type="submit" class="btn btn-success">Upload</button>
          </form>
        </div>
      </div>
    </div>
  </body>
</html>

Penjelasan :

  • @csrf : token CSRF wajib di form POST Laravel, tanpa ini request akan ditolak (error 419)
  • enctype="multipart/form-data" : wajib untuk form yang mengirim file
  • asset('storage/' . session('image')) : menampilkan gambar dari storage public, perlu php artisan storage:link tadi supaya bisa diakses

Menjalankan project Laravel di localhost

jalankan server:

php artisan serve

buka http://127.0.0.1:8000/image-upload, pilih file gambar, lalu klik Upload. kalau sukses, muncul notifikasi dan preview gambar.

Review kode: mengamankan dari vulnerability upload file

ada beberapa hal yang perlu diperhatikan supaya upload file tidak jadi celah keamanan:

1. Jangan pakai nama file asli dari client

kode seperti ini berbahaya karena bisa disisipi path traversal atau nama file yang menimpa file lain:

// jangan lakukan ini
$imageName = $request->image->getClientOriginalName();

pakai nama unik dari server:

$imageName = Str::uuid() . '.' . $request->image->extension();

2. Ambil ekstensi dari MIME, bukan nama file

getClientOriginalExtension() mengambil ekstensi dari nama file yang dikirim client, bisa dimanipulasi. sedangkan extension() membaca tipe file yang sebenarnya:

// lebih aman, baca dari signature file
$imageName = Str::uuid() . '.' . $request->image->extension();

3. Selalu validasi sebelum simpan

jangan pernah menyimpan file tanpa validasi tipe, ukuran, dan jangan eksekusi file yang diupload sebagai kode. untuk kasus ekstrem, simpan file di luar public atau gunakan whitelist MIME.

Pertanyaan yang Sering Diajukan

Kenapa pakai Storage Laravel, bukan move() ke public_path?

Storage memakai sistem filesystem yang fleksibel (local, S3, dll) dan mengelola nama file secara aman. move() langsung ke public_path rentan dan tidak scalable, apalagi kalau nanti pindah ke cloud storage.

Kenapa file upload saya 419 di Laravel?

Itu karena token CSRF tidak terkirim. Pastikan di form pakai @csrf, atau untuk API tambahkan middleware sanctum dan kirim header Accept: application/json.

Bagaimana cara validasi ukuran dan tipe file upload?

Pakai $request->validate() dengan aturan image, mimes, dan max. Contoh: image|mimes:jpeg,png,jpg,gif|max:2048. max dihitung dalam kilobyte.

Bagaimana menampilkan gambar yang diupload dari database?

Jalankan php artisan storage:link untuk membuat symlink public/storage ke storage/app/public, lalu akses lewat asset("storage/" . $path).

Sigit Nurhanafi avatar
Full-stack developer & technical writer. Berpengalaman di PHP, Laravel, NodeJS, MySQL, dan Python. Aktif menulis tutorial pemrograman dan maintaining open-source projects di PemburuKode sejak 2021.