Storage::exists
Reports whether a path exists on a disk, returning true for directories as well as files.
Storage::exists(string $path): bool- Added in Laravel 5.0
Parameters
| Parameter | Type | Description |
|---|---|---|
$$path | string | Path to check, relative to the disk's configured root. A single path — there is no array form. |
Return value
booltrue when a file or a directory exists at the path, false otherwise.
Errors
Nothing for a missing path. A driver-level failure such as an S3 credential error surfaces as a Flysystem exception rather than false.
Storage::exists asks the disk whether something is at the path. Storage::missing is the inverse and reads better in a guard clause: if (Storage::missing($path)) { abort(404); } beats a negated exists.
When to reach for it
Use it when you need to branch without reading the file: deciding whether to regenerate a thumbnail, reporting a broken attachment in a health check, choosing between a stored avatar and a default. Use it in tests through assertExists and assertMissing on a faked disk.
What trips people up
It is not a cheap call on a remote driver. Each invocation is an HTTP request to S3, so a loop over a thousand records makes a thousand requests. Prefer Storage::files($directory) once and check membership in PHP.
Do not use it to guard a read. exists followed by get is two round trips with a race between them, and get already returns null when the file is gone — checking that one return value is both cheaper and correct.
It returns true for directories, because it checks file existence and directory existence. Storage::exists('avatars') is true as soon as one avatar is in there, which surprises code that assumes a file. And it takes one path only — the array form belongs to assertExists, not to exists.
Example
<?php
use Illuminate\Support\Facades\Storage;
Storage::disk('public')->put('avatars/7.png', 'binary');
var_dump(Storage::disk('public')->exists('avatars/7.png')); // bool(true)
var_dump(Storage::disk('public')->exists('avatars/99.png')); // bool(false)
// missing() is the inverse and reads better as a guard.
if (Storage::disk('public')->missing('avatars/99.png')) {
echo 'falling back to the default avatar', PHP_EOL;
}
// Careful: directories count as existing too.
var_dump(Storage::disk('public')->exists('avatars')); // bool(true)
// One path per call - there is no array form here.
// Storage::exists(['a.png', 'b.png']); // not supportedWhen to use it
- Skip regenerating a thumbnail when the derived image is already on the disk.
- Report attachments referenced by a database row that are no longer present, in a nightly integrity check.
- Fall back to a default avatar image when the user's stored file is gone.
Frequently asked questions
What is the difference between Storage::exists and Storage::missing?
missing is simply the negation of exists, and it exists so guard clauses read naturally — if (Storage::missing($path)) { abort(404); }. Both take a single path and both return true for directories.
Can Storage::exists check several files at once?
No, it takes one path per call. The array form belongs to the testing assertions, Storage::disk('photos')->assertExists(['a.jpg', 'b.jpg']). To check many real files efficiently, list the directory once with Storage::files() and compare in PHP rather than making one request per path.
Is Storage::exists expensive?
On the local driver it is a stat call and effectively free; on S3, SFTP or FTP it is a network round trip. A loop calling it per database row is a common cause of slow admin pages — list the directory once instead.
More examples
Do not use exists to guard a read
On S3 each exists is a HEAD request and each get is a GET, so the guarded version doubles the network cost for no benefit — get already tells you the file was not there. The last block is the legitimate use: the decision is about whether to do work, and no contents are needed.
<?php
use Illuminate\Support\Facades\Storage;
// Two round trips, and the file can vanish in between.
if (Storage::disk('s3')->exists('invoices/42.pdf')) {
$pdf = Storage::disk('s3')->get('invoices/42.pdf'); // may still be null
}
// One round trip, no race, and it handles every failure mode.
if (($pdf = Storage::disk('s3')->get('invoices/42.pdf')) !== null) {
echo strlen($pdf).' bytes', PHP_EOL;
} else {
echo 'invoice not available', PHP_EOL;
}
// exists earns its place when you are NOT going to read the file.
if (Storage::disk('public')->missing("thumbs/{$id}.jpg")) {
// dispatch(new GenerateThumbnail($id));
echo 'thumbnail queued', PHP_EOL;
}Checking many files without one request each
files() returns the directory's contents in a single listing call, turning N network round trips into one. The sort at the end matters for anything you assert on or render: neither the local driver nor S3 promises a particular order, so an unsorted comparison passes on one machine and fails on another.
<?php
use Illuminate\Support\Facades\Storage;
$expected = ['avatars/1.png', 'avatars/2.png', 'avatars/3.png'];
// Slow: three HTTP requests against S3.
foreach ($expected as $path) {
Storage::disk('s3')->exists($path);
}
// Fast: one listing, then membership tests in PHP.
$present = Storage::disk('s3')->files('avatars');
$missing = array_values(array_diff($expected, $present));
print_r($missing);
// Expected output when 2.png is gone:
// Array
// (
// [0] => avatars/2.png
// )
// Sort any listing you intend to compare or display - the order
// a driver returns entries in is not guaranteed.
sort($present);
print_r($present);
Discussion