I have got user's avatars uploaded in Laravel storage. How can I access them and render them in a view?

The server is pointing all requests to /public, so how can I show them if they are in the /storage folder?

6 Answers 11

up vote 152 down vote accepted

The best approach is to create a symbolic link like @SlateEntropy very well pointed out in the answer below. To help with this, since version 5.3, Laravel includes a command which makes this incredibly easy to do:

php artisan storage:link

That creates a symlink from public/storage to storage/app/public for you and that's all there is to it. Now any file in /storage/app/public can be accessed via a link like:

http://somedomain.com/storage/image.jpg

If, for any reason, your can't create symbolic links (maybe you're on shared hosting, etc.) or you want to protect some files behind some access control logic, there is the alternative of having a special route that reads and serves the image. For example a simple closure route like this:

Route::get('storage/{filename}', function ($filename)
{
    $path = storage_path('public/' . $filename);

    if (!File::exists($path)) {
        abort(404);
    }

    $file = File::get($path);
    $type = File::mimeType($path);

    $response = Response::make($file, 200);
    $response->header("Content-Type", $type);

    return $response;
});

You can now access your files just as you would if you had a symlink:

http://somedomain.com/storage/image.jpg

If you're using the Intervention Image Library you can use its built in response method to make things more succinct:

Route::get('storage/{filename}', function ($filename)
{
    return Image::make(storage_path('public/' . $filename))->response();
});

WARNING

Keep in mind that by manually serving the files you're incurring a performance penalty, because you're going through the entire Laravel request lifecycle in order to read and send the file contents, which is considerably slower than having the HTTP server handle it.

upvote
  flag
That seems good. I would like to ask one more question. Is it recommended to store files like avatars, thumbs and so on (uploaded files from users) in storage? – Tadeáš Jílek
4 upvote
  flag
You can store them wherever you like, but unless those are private files I think you're better off storing them in the public directory. This way you'll be avoiding the overhead of having to compose an image response that could be handled much faster by the HTTP server. – Bogdan
upvote
  flag
So the recommended way is to store them in /public i guess, thanks. – Tadeáš Jílek
2 upvote
  flag
Personally I wouldn't store user files in the public folder, I find things much tidier and more manageable when the storage folder is used for storage. – SlateEntropy
2 upvote
  flag
I wouldn't recommend either of these because then you're adding the overhead of loading Laravel again and/or leveraging PHP to read each image. Using a symlink as described by CmdSft would be much faster. – user1960364
3 upvote
  flag
@user1960364 That's why the last paragraph strongly suggests to use the symlink approach. But the answer itself, while not the best solution from a performance standpoint, is still valid and may be the only approach for people running apps on shared servers where symlinks may be impossible to setup. The answer also showcases how you can serve files programatically if the need ever arises, maybe for serving encrypted files like asked in this question. – Bogdan
upvote
  flag
@user1960364 The symlink approach works well as long as you do not have any ACL on the files, which is rarely the case I might say – Mirko
upvote
  flag
great, I had been very problem with that until I used above command php artisan storage:link its working as well for me. – Mikel Williams
upvote
  flag
We use this for images embedded into emails. We find corporate spam filters that filter out image requests from S3, while images from the companies own sub domain are not cut. – Tom Andersen
upvote
  flag
Also note that this command needs to be ran on the server - not on your local dev machine. – Radmation
upvote
  flag
How can I run this command on server? When I connect to my server using PUTTY with ssh and run this command, nothing happens. I think the artisan command is unknown to server or putty, I don't know. – Khawar Raza
upvote
  flag
@Radmation the command needs to be run on all machines that run a particular app. That includes the dev machines, or else you can't expect the app to work the same locally and on the production server. – Bogdan
upvote
  flag
@KhawarRaza you need to be in your application directory to issue the command. So just cd /path/to/application and then run it. artisan is not a Linux command, it's a PHP script that you run as a command (hence the need to prepend php so that the PHP interpreter actually runs artisan). If you check your application's root directory you'll see that there is a file named artisan which is what is actually getting executed. – Bogdan
upvote
  flag
@Bogdan I tried that at first hand but nothing happens. Even I run the make controller command, no message no error and no controller file was created. I can see the artisan file in my project's root folder but no luck. What can be the problem? – Khawar Raza
upvote
  flag
@KhawarRaza it sounds like artisan is silently failing. I'm guessing even simply running php artisan will not output anything. Try running php -d display_errors artisan and see if you get an error message displayed. – Bogdan
upvote
  flag
@Bogdan I tried the above command but nothing happened :( – Khawar Raza
upvote
  flag
@Bogdan Would I have to use laravel SSH facade to run my artisan commands? Would it work this way? – Khawar Raza
upvote
  flag
Thank you. But the artisan-command didn't helped me, because I got an error: "In Filesystem.php line 228: symlink(): No such file or directory". It's strange, I am ubuntu user and I can create symlinks manually. Does somebody knows, what may be wrong with my environment? – Juljan

One option would be to create a symbolic link between a subfolder in your storage directory and public directory.

For example

ln -s /path/to/laravel/storage/avatars /path/to/laravel/public/avatars

This is also the method used by Envoyer, a deployment manager built by Taylor Otwell, the developer of Laravel.

upvote
  flag
I am bit new to this. Does this need to be run on terminal or be defined in a config file somewhere in laravel? – Gaurav Mehta
upvote
  flag
yes, you need to run this though the command line. It would need to be setup anywhere you deploy the app. – SlateEntropy
upvote
  flag
For Windows users, here is a standalone tool for that if you don't want to use CMD : github.com/amd989/Symlinker#downloads – David
upvote
  flag
Why would you use this if you don't have any control on what you're showing or you're distributing? Does it matter if your content becomes globally accessible? You probably better use a route as @Bogdan demonstrated in his answer and then use a middleware on this route to check if the user is allowed to get the content, or not. – roastedtoast
2 upvote
  flag
The question asked how to show user avatars stored in storage publicly, usually avatars don't require any sort of access control. If no security is required using any sort of middleware or route is just a wasted hit to your resources. It is also worth noting since Laravel 5.2 a separate file "disk" exists for public files (laravel.com/docs/5.2/filesystem) using symlinks. – SlateEntropy
4 upvote
  flag
There's an artisan command to do this in Laravel 5.3 : $ php artisan storage:link – Phil

If you are on windows you can run this command on cmd:

mklink /j /path/to/laravel/public/avatars /path/to/laravel/storage/avatars 

from: http://www.sevenforums.com/tutorials/278262-mklink-create-use-links-windows.html

According to Laravel 5.2 docs, your publicly accessible files should be put in directory

storage/app/public

To make them accessible from the web, you should create a symbolic link from public/storage to storage/app/public.

ln -s /path/to/laravel/storage/app/public /path/to/laravel/public/storage

Now you can create in your view an URL to the files using the asset helper:

echo asset('storage/file.txt');

If your using php just use the php syslink function symlink('/home/username/projectname/storage/app/public', '/home/username/public_html/storage')

change the username and projectname to the right names

It is good to save all the private images and docs in storage directory then you will have full control over file ether you can allow certain type of user to access the file or restrict.

tested on laravel 5.4 Make a route /docs and point to any controller method

    public function docs(){

    // any custom logic

   //check if user is logged in or user have permission to download this file etc


        $headers = [
          'Content-Type' => 'image/png',
       ];

    return response()->download(storage_path('app/users/documents/4YPa0bl0L01ey2jO2CTVzlfuBcrNyHE2TV8xakPk.png'), 'filename.png', $headers);
 }

when you will hit localhost:8000/docs file will be downloaded if there exists any

file must be in root/storage/app/users/documents directory according to above code

Not the answer you're looking for? Browse other questions tagged or ask your own question.