Setelah belajar session & cookie di episode sebelumnya, kali ini saya lanjut ke topik yang sering bikin bingung waktu awal belajar Laravel: file storage. Upload gambar, simpan dokumen, kelola file di server — semua lewat satu facade yang namanya Storage. Saya dulu sempat berpikir, ” Bukankah PHP biasa juga bisa move_uploaded_file()?” Tentu bisa. Tapi Laravel memberikan abstraksi yang jauh lebih rapi, lebih aman, dan — yang paling penting — bisa berganti “tukang simpan” tanpa mengubah kode sama sekali.
Konsep Disk: Bukan Hanya Harddisk
Dalam Laravel, disk adalah nama yang kita berikan untuk “tempat penyimpanan file”. Bukan harddisk fisik, melainkan sebuah konfigurasi yang mendefinisikan driver, root path, dan pengaturan lainnya. Bayangkan disk ini seperti alamat rumah: kita bilang “simpan ke disk ‘public'”, dan Laravel tahu harus mengirim file ke folder storage/app/public yang di-symlink ke public/storage.
Konfigurasi disk ada di config/filesystems.php. Di sana kita lihat beberapa disk bawaan:
local— penyimpanan di server lokal, folderstorage/app. Cocok untuk file internal aplikasi yang tidak boleh diakses publik.public— folderstorage/app/public. Cocok untuk file yang harus bisa diakses lewat browser (gambar profil, banner, dll).s3— Amazon S3 (atau compatible seperti MinIO). Untuk produksi yang butuh penyimpanan terdistribusi.
Kita bisa menambah disk sendiri sesuai kebutuhan. Misalnya disk khusus backup, atau disk yang menunjuk ke Google Cloud Storage. Intinya: setiap disk punya driver (local, s3, ftp, dll) yang menentukan cara file dikirim/diambil.
Mengunggah File dari Form
Mari kita buat fitur upload gambar sederhana. Misalnya, user bisa mengunggah foto profil.
Pertama, route:
Route::get('/profile/photo', [ProfileController::class, 'showForm']);
Route::post('/profile/photo', [ProfileController::class, 'upload']);Lalu di controller:
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
public function upload(Request $request)
{
$request->validate([
'photo' => 'required|image|mimes:jpeg,png|max:2048',
]);
$path = $request->file('photo')->store('avatars', 'public');
// Simpan path ke database
auth()->user()->update([
'avatar' => $path,
]);
return redirect('/profile')->with('success', 'Foto berhasil diunggah!');
}Perhatikan baris $request->file('photo')->store('avatars', 'public'). Di situ terjadi banyak hal: Laravel menerima file dari request, memvalidasi tipe & ukuran, lalu menyimpannya ke disk public di subfolder avatars. Nama file otomatis dibuat unik (menggunakan Str::uuid()) sehingga tidak perlu khawatir tabrakan nama. Kita tinggal simpan path-nya ke database.
Menampilkan & Menghapus File
Menampilkan file yang tersimpan di disk public cukup pakai helper Storage::url():
<img src="<?= Storage::url(auth()->user()->avatar) ?>" alt="Foto Profil"> // Atau kalau pakai Asset facade untuk URL absolut: use Illuminate\Support\Facades\Asset; <img src="<?= Asset::storage(auth()->user()->avatar) ?>" alt="Foto Profil">
Menghapus file juga simpel:
use Illuminate\Support\Facades\Storage;
// Hapus satu file
Storage::disk('public')->delete($user->avatar);
// Hapus beberapa sekaligus
Storage::disk('public')->delete([
'avatars/photo1.jpg',
'avatars/photo2.jpg',
]);Yang saya sukai dari API Laravel ini: namanya natural. delete() hapus file, exists() cek keberadaan, get() ambil isi file sebagai string, put() tulis isi ke file. Tidak perlu ingat fungsi PHP bawaan yang berbeda-beda tiap operasi.
Validasi File: Jangan Percaya Input User
Validasi file adalah lapisan keamanan kritis. Tanpa validasi, siapa saja bisa mengunggah file PHP berbahaya ke server kita. Laravel menyediakan rule khusus untuk file:
$request->validate([
'document' => 'required|file|mimes:pdf,docx|max:10240', // max 10MB
'image' => 'required|image|dimensions:min_width=100,min_height=100',
'avatar' => 'file|nullable|accepted_files:jpg,png,webp',
]);
// Dengan File facade untuk kontrol lebih detail
use Illuminate\Http\UploadedFile;
$validated = $request->validate([
'photo' => [
'required',
'image',
function ($attribute, $value, $fail) {
if ($value->getClientMimeType() !== 'image/jpeg') {
$fail('Hanya file JPEG yang diterima.');
}
},
],
]);Rule mimes memeriksa ekstensi file. Rule image memastikan file benar-benar gambar (bukan file .jpg yang isinya script). Rule max membatasi ukuran dalam kilobyte. Untuk validasi lebih ketat, kita bisa pakai closure seperti contoh di atas.
Menyimpan ke S3 (atau Storage Cloud Lainnya)
Inilah kekuatan abstraksi disk. Saat aplikasi masih di lingkungan development, file tersimpan di folder lokal. Saat naik ke production, kita cukup ganti konfigurasi di .env:
FILESYSTEM_DISK=s3 AWS_ACCESS_KEY_ID=your-key AWS_SECRET_ACCESS_KEY=your-secret AWS_DEFAULT_REGION=ap-southeast-1 AWS_BUCKET=my-production-bucket
Seluruh kode upload, hapus, dan baca file tidak perlu diubah sama sekali. Method store(), delete(), url() tetap dipanggil — Laravel hanya mengganti driver di belakang layar. Ini yang saya maksud sebagai “berganti tukang simpan tanpa ubah kode”. Prinsip ini dikenal sebagai dependency inversion: kode kita bergantung pada abstraksi (interface Filesystem), bukan implementasi konkret (local disk atau S3).
Di lingkungan development, kita bisa pakai MinIO (S3-compatible) untuk mensimulasi S3 tanpa harus bayar AWS. Cukup set endpoint ke http://localhost:9000 di config filesystem.
File Access: Visibility & Temporary URLs
Laravel mengatur akses file lewat visibility: public (bisa diakses semua orang lewat URL) dan private (hanya bisa diakses dari kode). Untuk S3, visibility private berarti file tidak punya URL publik — kita harus membuat temporary URL yang expired dalam waktu tertentu:
// Buat URL sementara yang berlaku 15 menit
$url = Storage::disk('s3')->temporaryUrl(
'invoices/invoice-2026.pdf',
now()->addMinutes(15)
);
// Gunakan di Blade
<a href="<?= $url ?>">Download Invoice</a>Ini berguna untuk file sensitif: laporan keuangan, dokumen legal, atau foto privat. User hanya mendapat URL yang berlaku singkat — setelah expired, akses ditolak. Jauh lebih aman daripada membiarkan file bisa diakses selamanya.
Facades & Helper yang Sering Dipakai
Ringkasan cepat method Storage yang paling sering saya gunakan:
| Method | Fungsi |
|---|---|
Storage::put($path, $content) | Tulis isi string ke file |
Storage::get($path) | Baca isi file sebagai string |
Storage::exists($path) | Cek apakah file ada |
Storage::delete($path) | Hapus file |
Storage::copy($from, $to) | Salin file |
Storage::move($from, $to) | Pindahkan file |
Storage::files($dir) | List semua file di direktori |
Storage::directories($dir) | List semua subdirektori |
Storage::makeDirectory($dir) | Buat direktori baru |
Storage::deleteDirectory($dir) | Hapus direktori beserta isinya |
Pengalaman Saya: Pelajaran yang Dipetik
Waktu pertama kali mengelola file di Laravel, saya sempat salah satu hal: lupa menjalankan php artisan storage:link. Hasilnya, file berhasil diunggah ke folder storage/app/public, tapi tidak bisa diakses dari browser karena tidak ada symlink ke public/storage. Error 404 terus menerus sampai saya sadar symlink-nya belum dibuat. Sepele, tapi bikin frustasi.
Pelajaran kedua: jangan pernah menyimpan path file mentah di database tanpa memikirkan migrasi. Jika suatu hari kita pindah dari local ke S3, semua path yang sudah tersimpan tetap valid — Storage::url() akan mengembalikan URL yang benar berdasarkan disk yang aktif. Tapi kalau kita menyimpan URL absolut secara manual (https://mysite.com/storage/avatars/xxx.jpg), semua data itu harus di-update satu per satu. Itulah mengapa Laravel merekomendasikan menyimpan path relatif, bukan URL absolut.
Pelajaran ketiga: jangan lupa validasi. Saya pernah menemukan bahwa tim lain mengunggah file PHP sebagai “gambar” karena tidak ada validasi mimes. Untungnya server tidak mengeksekusi file PHP di folder uploads, tapi tetap saja — itu celah keamanan yang seharusnya tidak ada. Rule image atau mimes di Laravel sudah menangani ini, asalkan kita benar-benar memasangnya.
Kesimpulan
File storage di Laravel mengajarkan saya pentingnya abstraksi yang baik. Satu API yang konsisten — store(), delete(), url() — bisa bekerja untuk local disk, S3, FTP, atau storage cloud lainnya. Yang perlu diingat: konfigurasi disk di .env dan config/filesystems.php, jalankan storage:link untuk disk public, dan selalu validasi file dari user. Di episode berikutnya, kita akan belajar tentang email dan notifikasi — bagaimana Laravel memudahkan pengiriman email lewat Mailable dan notification channel.
Komentar Terbaru