Need to store files per user in Laravel? Let's discuss storing uploads for users in a separate directory to avoid collisions and keep our filesystem organised.
I'll start by mentioning that this solution isn't going to involve creating a custom filesystem adapter or anything more complicated than that. Why? From experience, when I've tried to automatically append a user ID onto the directory to store a file, my codebase has become more complex than needed.
So, let's take a look at the process of uploading files and storing each user's files separately.
Here's the basic upload form we're going to be using, along with the route:
Route::get('/', function (Request $request) {
return view('upload');
});
<form action="/upload" method="post" enctype="multipart/form-data">
@csrf
<input type="file" name="file" id="file">
<button type="submit">
Upload
</button>
</form>
Here's an example of how we'd usually store a file, choosing to add it to a files
directory:
Route::post('/upload', function (Request $request) {
$request->file('file')->store('files');
return back();
});
This takes the file, generates a filename for us (like JRwtxrjK4cxoLa6U0en2GZxx8pqoQOflZpmttLRD.png) and stores it in the app/private
directory.
As I mentioned earlier, doing anything other than appending on the ID makes our application wayy more complicated than it needs to be.
So, here's what I'd do:
Route::post('/upload', function (Request $request) {
$request->file('file')->store('files/' . auth()->id());
return back();
});
As you can see, the only change I've made is to add on the user's ID.
If you were storing the file in your database, you may do something like this:
Route::post('/upload', function (Request $request) {
$request->user()->files()->create([
'path' => $request->file('file')->store('files/' . auth()->id())
]);
return back();
});
This would then fill the path
column of your model with the full path to the file (e.g. files/1/file.png). That gives you the full path to the user-specific upload, so you're able to use this to download a file easily, like this:
Route::get('/files/{file}/download', function (File $file, Request $request) {
return Storage::download($file->path);
});
This solution may seem a little underwhelming at first, but trust me — long term, it's not worth creating a custom filesystem to handle this stuff.
If you make it any more complex than this, something will get in the way later down the line!