Aplikasi nyata tidak hanya menyimpan data di database: foto profil, faktur PDF, dan lampiran email semuanya hidup sebagai berkas fisik di suatu tempat. Laravel membungkus lokasi penyimpanan tersebut dalam konsep disk — sekumpulan konfigurasi bernama yang menentukan ke mana berkas ditulis dan bagaimana berkas tersebut diambil. Seluruh konfigurasi itu berada dalam satu file, config/filesystems.php. Chapter ini membedah file tersebut satu per satu, lalu kita buktikan perilakunya lewat runner kecil yang mem-bootstrap aplikasi Laravel sungguhan.
Disk: Abstraksi Lokasi Penyimpanan #
Disk adalah pemetaan nama → konfigurasi. Satu disk menyatakan driver (local, ftp, sftp, s3), folder root tempat berkas ditulis, dan opsi tambahan seperti url dan visibility. Kode aplikasi tidak pernah menyebut path absolut — kode hanya menyebut nama disk dan path relatif. Karena itu kita bisa memindahkan seluruh berkas dari harddisk server ke Amazon S3 hanya dengan mengubah konfigurasi, tanpa menyentuh satu baris pun controller.
Laravel 13 membawa tiga disk bawaan di file konfigurasi: local, public, dan s3. Disk mana yang dipakai ketika kode memanggil Storage::put() tanpa menyebut disk? Itu ditentukan oleh key default.
Membedah config/filesystems.php #
Berikut isi config/filesystems.php pada instalasi Laravel 13 — komentar beserta beberapa key opsional dipangkas agar fokus pada bagian penting:
return [
'default' => env('FILESYSTEM_DISK', 'local'),
'disks' => [
'local' => [
'driver' => 'local',
'root' => storage_path('app/private'),
'serve' => true,
'throw' => false,
],
'public' => [
'driver' => 'local',
'root' => storage_path('app/public'),
'url' => rtrim(env('APP_URL', 'http://localhost'), '/').'/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'),
],
],
'links' => [
public_path('storage') => storage_path('app/public'),
],
];Mari kita bedah baris per baris:
'default' => env('FILESYSTEM_DISK', 'local')— disk yang dipakai facadeStorageketika tidak ada disk yang disebut eksplisit. Nilainya dibaca dari environment variableFILESYSTEM_DISK; file.envbawaan berisiFILESYSTEM_DISK=local.- Disk
local— driverlocaldengan rootstorage/app/private. Folder ini berada di luarpublic/, sehingga tidak akan pernah bisa diakses lewat URL browser. Opsiserveakan kita bahas di chapter Temporary URLs. - Disk
public— driverlocaljuga, tetapi root-nyastorage/app/publicdan memiliki keyurl. Berkas di disk ini dirancang untuk dibagikan ke publik lewat symlink yang mendaftarkan folder tersebut kepublic/storage(lihat keylinksdi bagian bawah file). - Disk
s3— templat konfigurasi cloud storage; seluruh kredensial dibaca dari environment variable sehingga tidak ada rahasia yang tercetak di file konfigurasi.
Membaca Konfigurasi Runtime #
Kita buktikan bahwa konfigurasi di atas benar-benar menjadi dasar kerja facade Storage. Runner berikut membaca konfigurasi aktif, lalu memanggil path() untuk melihat lokasi absolut yang dihasilkan (bagian awal path yang menunjuk folder storage/app dipangkas agar output ringkas):
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
echo 'disk default : '.config('filesystems.default')."\n";
echo 'root local : '.Str::after(config('filesystems.disks.local.root'), storage_path('app'))."\n";
echo 'root public : '.Str::after(config('filesystems.disks.public.root'), storage_path('app'))."\n";
echo 'visibility public: '.config('filesystems.disks.public.visibility')."\n";
echo 'url public : '.config('filesystems.disks.public.url')."\n";
echo 'path() local : '.Str::after(Storage::disk('local')->path('dokumen/laporan.txt'), storage_path('app'))."\n";
echo 'path() public : '.Str::after(Storage::disk('public')->path('dokumen/laporan.txt'), storage_path('app'))."\n";Output yang Dihasilkan:
disk default : local
root local : /private
root public : /public
visibility public: public
url public : http://localhost:8000/storage
path() local : /private/dokumen/laporan.txt
path() public : /public/dokumen/laporan.txtPerhatikan dua baris terakhir: string path yang sama, 'dokumen/laporan.txt', menghasilkan dua lokasi absolut yang berbeda karena masing-masing disk punya root sendiri. Inilah inti abstraksi disk — path relatif selalu ditafsirkan relatif terhadap root disk yang aktif.
Membuat Disk Kustom #
Aplikasi nyata sering butuh lebih dari dua disk: arsip tahunan, berkas sementara, atau folder per tenant. Kita cukup menambah entri baru pada array disks:
'arsip' => [
'driver' => 'local',
'root' => storage_path('app/arsip'),
'visibility' => 'private',
],Disk arsip kini hidup dan bisa dipakai tanpa konfigurasi tambahan apa pun:
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
Storage::disk('arsip')->put('buku-tahun-2024.txt', 'arsip lama');
echo 'arsip ada : '.var_export(Storage::disk('arsip')->exists('buku-tahun-2024.txt'), true)."\n";
echo 'path() arsip : '.Str::after(Storage::disk('arsip')->path('buku-tahun-2024.txt'), storage_path('app'))."\n";Output yang Dihasilkan:
arsip ada : true
path() arsip : /arsip/buku-tahun-2024.txtBerkas buku-tahun-2024.txt kini tersimpan di storage/app/arsip, terpisah total dari disk local maupun public. Satu folder root bisa dipakai banyak disk selama kita sengaja mendesainnya begitu, tetapi memisahkan root per kebutuhan membuat batas tiap jenis berkas tetap jelas. Momen yang tepat menambah disk adalah ketika satu kelompok berkas punya umur atau kebijakan akses sendiri — berkas sementara yang rutin dipangkas, arsip yang hanya dibaca, atau folder upload publik yang dipisahkan supaya mudah dipindahkan ke penyimpanan lain.
Key throw dan report #
Dua key kecil pada tiap disk mengatur perilaku saat operasi gagal. Dengan throw => false (nilai bawaan), Storage::put() pada disk yang penuh hanya mengembalikan false — request tetap lanjut dan error dilaporkan sebagai log. Mengubahnya menjadi throw => true membuat exception UnableToWriteFile dilempar, sehingga kegagalan penyimpanan langsung terasa sebagai error 500 alih-alih data yang diam-diam hilang. Key report mengatur apakah kegagalan tersebut dilaporkan ke exception handler untuk logging. Untuk berkas yang kritikal terhadap bisnis (faktur, bukti pembayaran), throw => true umumnya pilihan yang lebih aman; untuk thumbnail yang bisa di-generate ulang, nilai bawaan sudah memadai.
Dengan konfigurasi yang sudah dipahami, kita siap berkenalan dengan masing-masing disk bawaan: kapan memakai local, kapan public, dan bagaimana memilih disk pada tiap operasi penyimpanan. Seluruh opsi konfigurasi terdokumentasi resmi di dokumentasi Filesystem
Laravel.