Skip to main content
Tutorial Free

Laravel File Uploads: Save Filename in DB with Folder and URL?

June 16, 2023
3 min read

When uploading files with Laravel, how to store the filename in the DB? Should you store filename.png? Or, include the folder of avatars/filename.png? Or, the full path https://website.com/avatars/filename.png? Let me tell you my opinion.

This tutorial will have an avatar field on the register page and an avatar column in the users table. Let's see how we can save it.


The easiest way is to store just the filename and create a separate storage disk.

config/filesystems.php:

return [
 
// ...
 
'disks' => [
 
// ...
 
'avatars' => [
'driver' => 'local',
'root' => storage_path('app/public/avatars'),
'url' => env('APP_URL').'/storage/avatars',
'visibility' => 'public',
'throw' => false,
],
 
],
 
// ...
 
];

Then, when storing the file we need to specify the new avatars disk.

app/Http/Controllers/Auth/RegisteredUserController.php:

class RegisteredUserController extends Controller
{
// ...
 
public function store(Request $request): RedirectResponse
{
$request->validate([
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'email', 'max:255', 'unique:'.User::class],
'password' => ['required', 'confirmed', Rules\Password::defaults()],
'avatar' => ['nullable', 'image'],
]);
 
if ($request->hasFile('avatar')) {
$avatar = $request->file('avatar')->store(options: 'avatars');
}
 
$user = User::create([
'name' => $request->name,
'email' => $request->email,
'password' => Hash::make($request->password),
'avatar' => $avatar ?? null,
]);
 
// ...
}
}

This way, your full URL filename https://website.com/storage/avatars/filename.png consists of three things:

  • Domain: https://website.com is stored in your APP_URL in .env file: so it is flexibly different for your local and production servers
  • Folder: /storage/avatars is in the config('disks.avatars.url') which corresponds to the internal structure of /storage/app/public/avatars described in the same config file. Both also can be flexibly changed if needed.
  • Filename: filename.png is the only thing actually landing in the DB column

To get the URL for the image in the Blade file, we would use URL method on the Storage facade providing the disk.

<img src="{{ Storage::disk('avatars')->url(Auth::user()->avatar) }}" alt="{{ Auth::user()->name }}" />

But what if, after some time, you would need to go from local disks to, let's say, Amazon S3?

The only change you would need to make is to change the disk, and maybe instead of the url, use the temporaryUrl method to provide the expiration time for the link.

<img src="{{ Storage::disk('s3')->temporaryUrl(Auth::user()->avatar, now()->addMinutes(5)) }}" alt="{{ Auth::user()->name }}" />

Enjoyed This Tutorial?

Get access to all premium tutorials, video and text courses, and exclusive Laravel resources. Join our community of 10,000+ developers.

Comments & Discussion

RA
Richard A. Hoyle ✓ Link copied!

Ok in what format should we store the avatar "table->string, test, binary, integer, bigInteger, which of these do you thank would be the best for storing the avatar in the database ?

PK
Povilas Korop ✓ Link copied!

I personally store filename as string, and file itself in the filesystem on disk. I don't store the avatar itself as binary in the database.

RA
Richard A. Hoyle ✓ Link copied!

I am able to save the image to the storage\app\public\avatars and after running the php artisan storage:link command it is in the public\storage\avatars file however it is not going to the database. How do you suggest we send it to the database. Please help with a code hint.

I have this in the database users table: $table->string('avatar')->nullable();

N
Nerijus ✓ Link copied!

how are you saving it to the db?

RA
Richard A. Hoyle ✓ Link copied!

RegisteredUserController

 
public function store(Request $request): RedirectResponse
{
$request->validate([
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'email', 'max:255', 'unique:'.User::class],
'password' => ['required', 'confirmed', Rules\Password::defaults()],
'timezone' => ['required', Rule::in(array_flip(timezone_identifiers_list()))],
'avatar' => ['nullable', 'image'],
]);
 
if ($request->hasFile('avatar')) {
$avatar = $request->file('avatar')->store(options: 'avatars');
}
 
$user = User::create([
'name' => $request->name,
'email' => $request->email,
'password' => Hash::make($request->password),
'timezone' => timezone_identifiers_list()[$request->input('timezone', 'America/Chicago')],
'avatar' => $avatar ?? null,
]);
N
Nerijus ✓ Link copied!

please format your code using markdown, can't read it like that. And provide more code. How is $avatar set? Do you have avatar in the fillable?

RA
Richard A. Hoyle ✓ Link copied!

how is this now? This is my first time using markdown.

N
Nerijus ✓ Link copied!

did you add fillable to the user model for the avatar? it's just you made somewhere a simple mistake

RA
Richard A. Hoyle ✓ Link copied!

YES

RA
Richard A. Hoyle ✓ Link copied!
 
protected $fillable = [
'name',
'email',
'password',
'is_admin',
'is_staff',
'timezone',
'avatar',
];
RA
Richard A. Hoyle ✓ Link copied!

Would using the Uuid in the user make any difference I do not thank so but thought I might put that out here since I am using it as well

N
Nerijus ✓ Link copied!

no

RA
Richard A. Hoyle ✓ Link copied!

In the register.blade.php file I have

 
<form method="POST" action="{{ route('register') }}" enctype="multipart/form-data">
 
<!-- Avatar Select -->
<div class="mt-4 form-group row">
<label for="avatar" class="col-md-4 col-form-label text-md-right">{{ __('Avatar (optional)') }}</label>
 
<div class="col-md-6">
<input id="avatar" type="file" class="form-control" name="avatar">
</div>
</div>
RA
Richard A. Hoyle ✓ Link copied!

I got it to work thanks the photo is now comming up on the dashboard page and it is in the database as well

Thanks

RA
Richard A. Hoyle ✓ Link copied!

How can I put an image change or update In the profile\partials\update-profile-information-form.blade.php file any workable code would be helpfull. I tryed to use the code from jetstream but it did not work.

K
Koda ✓ Link copied!

Great Short Tutorial. Thank you @Povilas

I have a few questions about this

Where do you see the advantage when I create a disk and

$avatar = $request->file('avatar')->store(options: 'avatars');

instead of (without Disk)

$avatar = $request->file('avatar')->store('avatars', 'public');

And is there a reason to work with Storage instead of asset in the blade?

With Storage I get https://www.laravel...., and with asset() I get https://backend.laravel (I have multiple Subdomains in my project)

What do you think about Scoped Filesystem so that I use this instead of a new disk "avatars"? https://laravel.com/docs/10.x/filesystem#scoped-and-read-only-filesystems

We'd Love Your Feedback

Tell us what you like or what we can improve

Feel free to share anything you like or dislike about this page or the platform in general.