I am new to Laravel and trying to store private images so that only authenticated users can access them. Firstly I stored images in Public/UserImages folder. But here all th
To have private files (images), you need to serve the files through a route => controller flow. And your auth middleware will handle authentication, and permission. If further authorization are needed you handle it in the controller.
Here we can have one route that handle all our files [i personally don't prefer that]. We can do that using such a route (it's like a wildcard).
Route::get('/storage/{filePath}', 'FileController@fileStorageServe')
->where(['filePath' => '.*'])
You can name it too like that:
Route::get('/storage/{fileName}', 'FileController@fileStorageServe')
->where(['fileName' => '.*'])->name('storage.gallery.file');
Otherwise we create a route for each type/category of files: (advantage: you will be able to control better the accessibility. (Each route and type of resources and it's rules. If you want to achieve that with the wild card route (let me call it that) you need to have conditional blocks (if else, handling all different situations. It's unnecessary operations [going directly to the right block, when the routes are separate is better, plus out of that, it allow you to organize better the permissions handling]).
Route::get('/storage/gallery/{file}', 'System\FileController@getGalleryImage')
->name('storage.gallery.image');
The wild card one
exists($filePath)){ // note that disk()->exists() expect a relative path, from your disk root path. so in our example we pass directly the path (/.../laravelProject/storage/app) is the default one (referenced with the helper storage_path('app')
abort('404'); // we redirect to 404 page if it doesn't exist
}
//file exist let serve it
// if there is parameters [you can change the files, depending on them. ex serving different content to different regions, or to mobile and desktop ...etc] // repetitive things can be handled through helpers [make helpers]
return response()->file(storage_path('app'.DIRECTORY_SEPARATOR.($filePath))); // the response()->file() will add the necessary headers in our place (no headers are needed to be provided for images (it's done automatically) expected hearder is of form => ['Content-Type' => 'image/png'];
// big note here don't use Storage::url() // it's not working correctly.
}
The per route one
(big difference, is the parameter, now it reference just the file name, and not the relative path to storage disk root)
exists($file)){ // as mentionned precise relatively to storage disk root (this one work well not like Storage:url()
abort('404');
}
// if there is parameters [you can change the files, depending on them. ex serving different content to different regions, or to mobile and desktop ...etc] // repetitive things can be handled through helpers [make helpers]
return response()->file(storage_path('app'.DIRECTORY_SEPARATOR.($file))); // the response()->file() will add the necessary headers in our place
}
Now you can check by forming the correct url (go to storage copy past the file name, and form your route. it should show you the image)
One last thing left:
the wild card one
Note that routeName here in the example above will be storage.file, and fileParam would be filePath. $storageRelativePath for example you get from the db (generally that's what it will be).
The per route
Same but we provide only the filename.
Notes: The best way to send such a response, is using response()->file();. That what you will find in the 5.7 doc. That performance wise against Image::make($storagePath)->response();. Unless you need to modify it in the fly.
You can check my article in medium: https://medium.com/@allalmohamedlamine/how-to-serve-images-and-files-privatly-in-laravel-5-7-a4b469f0f706