Solving the public
Prefix Issue in Laravel File Storage
Struggling with file paths when uploading files in Laravel? Here’s a quick guide to storing files correctly and retrieving their URLs without hassle. The key is to use the appropriate disk to avoid unnecessary prefixes, making migrations to other storage drivers seamless.
Understanding the Default Disk Configuration
By default, Laravel’s filesystems.php
configuration file might look like this:
'disks' => [
'local' => [
'driver' => 'local',
'root' => storage_path('app'),
'throw' => false,
],
'public' => [
'driver' => 'local',
'root' => storage_path('app/public'),
'url' => env('APP_URL') . '/storage',
'visibility' => 'public',
'throw' => false,
],
],
When using the local
driver, the returned paths in the file storage methods include a public
prefix, which can be cumbersome to handle. This issue also complicates switching to an S3 storage driver. Understanding how to address this is crucial for smooth file management in Laravel.
The Issue
Using Laravel’s store
method with the default disk returns a relative path based on the disk configuration. This means files are stored in storage/app/public/avatars
, and the returned path includes the public
prefix. For example:
public function update(Request $request): string
{
// The returned path is of the form 'public/avatars/AJDSLAJSD.jpg'
$path = $request->file('avatar')->store('public/avatars');
// .. store path in model
$user->avatar = str_replace('public/', '', $path);
}
However, in the database, we want to store avatars/ASDASD.jpg
without the public
prefix because we use it like this: Storage::url('avatars/ASDASD.jpg')
. Including the public
prefix in the path within store
will also cause issues when switching to an S3 storage driver later, as we don’t want the public
prefix there. If you change your default storage driver to S3, you would need to change the code to:
public function update(Request $request): string
{
// The returned path is of the form 'public/avatars/AJDSLAJSD.jpg'
$path = $request->file('avatar')->store('avatars', 'S3');
// .. store path in model
$user->avatar = $path;
}
Switching storage driver would suddenly become cumbersome and error-prone.
The Solution
Store files in the public
disk explicitly to avoid path issues:
public function update(Request $request): string
{
// The returned path is of the form 'avatars/AJDSLAJSD.jpg'
$path = $request->file('avatar')->store('avatars', 'public');
// store in model..
$user->avatar = $path;
}
This small change is a big win.
- We get rid of
store('public/avatars')
which makes only sense forlocal
driver - We get the clean relative path that should be stored in the database
- Changing the storage driver is easy now.
Retrieve File URL:
$avatarPath = 'avatars/XXX.jpg';
$avatarUrl = Storage::url($avatarPath);
echo $avatarUrl; // Outputs /storage/avatars/XXX.jpg for "local" driver
Switching to S3:
Image you want to use now an S3 solution instead of local path.
Configure S3 Disk:
'disks' => [
'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'),
],
],
Update File Upload Method:
public function update(Request $request): string
{
// The returned path is of the form 'avatars/AJDSLAJSD.jpg'
$path = $request->file('avatar')->store('avatars', 's3');
// store in model..
$user->avatar = $path;
}
Retrieve S3 URL:
$avatarPath = 'avatars/XXX.jpg';
$avatarUrl = Storage::disk('s3')->url($avatarPath);
echo $avatarUrl; // Outputs the full S3 URL to the avatar image
By using Laravel’s storage system, you can handle file uploads efficiently and switch storage backends with ease.