siannone
siannone

Reputation: 6763

Laravel: route to storage folder

What I'm trying to achieve is to make a route to the storage folder so that I can access it even if it's not under the public directory.

For example, user's avatars are located in app\storage\user\avatars\avatar.jpg and I would like to make a route so that I can access those images from something like http://localhost/user/avatars/avatar.jpg.

How can I achieve this?

Upvotes: 0

Views: 11663

Answers (3)

Mario Campa
Mario Campa

Reputation: 4252

A better way to achieve this is using an alias or rewrite on your web server.

If you are using nginx, add this to your server block:

# Rewrite for user avatars
location /user/ {
    root /var/www/laravel/app/storage/user;
}

For Apache use 'Alias'

Alias /user /var/www/laravel/app/storage/user

Doing it this way is faster and better performance, than bootstrapping Laravel for every image request.

Upvotes: 0

Neo
Neo

Reputation: 886

Firstly, i would recommend moving the avatars folder to somewhere more publicly accessible. But as its Laravel, you can achieve whatever you want.

Route::get('user/avatars/{filename}', function($filename)
{
    $filePath = storage_path().'/user/avatars/'.$filename;

    if ( ! File::exists($filePath) or ( ! $mimeType = getImageContentType($filePath)))
    {
        return Response::make("File does not exist.", 404);
    }

    $fileContents = File::get($filePath);

    return Response::make($fileContents, 200, array('Content-Type' => $mimeType));
});

Then somewhere add this custom helper function:

function getImageContentType($file)
{
    $mime = exif_imagetype($file);

    if ($mime === IMAGETYPE_JPEG) 
        $contentType = 'image/jpeg';

    elseif ($mime === IMAGETYPE_GIF)
        $contentType = 'image/gif';

    else if ($mime === IMAGETYPE_PNG)
        $contentType = 'image/png';

    else
        $contentType = false;

    return $contentType;
}

It may be worth noting that there are security concerns with the method you are proposing and the solutions.

Upvotes: 3

Antonio Carlos Ribeiro
Antonio Carlos Ribeiro

Reputation: 87789

The best way of doing such thing is to use Response::download() to serve files outside your public folder:

Create a router:

Route::get('/user/avatars/{avatarName}', 'AvatarServerController@downloadAvatar');

And in this could be the controller controller you do

class AvatarServerController extends Controller {

    public function downloadAvatar($avatarName)
    {
        $fileName = storage_path()."/user/avatars/$avatarName";

        if (File::exists($fileName))
        {
            return Response::download(fileName);
        }

        return Redirect::route('home')->withMessage('Avatar file not found.');
    }

}

But, still giving access to anything else than the public folder may be a serious security risk, that's why Laravel is build this way. But there are some other options:

1) Create a symlink:

ln -s /var/www/site/app/storage/user/avatars /var/www/site/public/user/avatars

And then use them directly:

HTML::image('user/avatar/avatar.jpg', 'User avatar');

3) Create a virtual host alias pointing to your app/storage/user/avatars directory.

Upvotes: 4

Related Questions