Laravel: load images stored outside 'public' folder

10,516

Solution 1

How about this (just tested it myself and it works):

The view:

<img src="/images/theImage.png">

Routes.php:

Route::get('images/{image}', function($image = null)
{
    $path = storage_path().'/imageFolder/' . $image;
    if (file_exists($path)) { 
        return Response::download($path);
    }
});

Solution 2

Here is a slightly modified version of @Mattias answer. Assume the file is in the storage/app/avatars folder which is outside the web root.

<img src="/avatars/3">

Route::get('/avatars/{userId}', function($image = null)
{
  $path = storage_path().'/app/avatars/' . $image.'.jpg';
  if (file_exists($path)) {
    return response()->file($path);
  }
});

Probably needs and else. Also I have wrapped mine inside the middleware auth Route Group which means you have to be logged in to see (my requirements) but I could do with more control over when it is made visible, perhaps alter the middleware.

EDIT Forgot to mention that this is for Laravel 5.3.

Solution 3

Here's what I came up with:

I'm trying to show the images in the view, not download. Here's what I came up with:

  • Note that these images are stored above the public folder, which is why we have to take extra steps to display the image in the view.

The view

{{ HTML::image($user->getProfileImage(), '', array('height' => '50px')) }}

The model

/**
 * Get profile image
 *
 * 
 *
 * @return string
 */
public function getProfileImage()
{   
    if(!empty($this->profile_image) && File::exists($this->profile_image))
    {       

        $subdomain = subdomain();

        // Get the filename from the full path
        $filename = basename($this->profile_image);

        return 'images/image.php?id='.$subdomain.'&imageid='.$filename;
    }

    return 'images/missing.png';
}

public/images/image.php

<?php

$tenantId = $_GET["id"];
$imageId = $_GET["imageid"];

$path = __DIR__.'/../../app/storage/tenants/' . $tenantId . '/images/profile/' . $imageId; 

 // Prepare content headers
$finfo = finfo_open(FILEINFO_MIME_TYPE); 
$mime = finfo_file($finfo, $path);
$length = filesize($path);

header ("content-type: $mime"); 
header ("content-length: $length"); 

// @TODO: Cache images generated from this php file

readfile($path); 
exit;
?> 

If somebody has a better way, please enlighten us!! I'm very interested.

Share:
10,516
kablamus
Author by

kablamus

Updated on June 28, 2022

Comments

  • kablamus
    kablamus almost 2 years

    I'm trying to display an image stored outside the 'public' folder in my view. These are simple profile images whose paths are stored in the DB. The path looks like

    /Users/myuser/Documents/Sites/myapp/app/storage/tenants/user2/images/52d645738fb9d-128-Profile (Color) copy.jpg
    

    Since the image is stored a DB column for each user, my first thought was to create an Accessor in the User model to return the image. I tried:

    public function getProfileImage()
    {   
        if(!empty($this->profile_image))
        {   
    
            return readfile($this->profile_image);
        }
    
        return null;
    }
    

    That produced unreadable characters in the view. I also tried file_get_contents() in place of read file. Any suggestions about how this might be accomplished?

  • martyn
    martyn almost 9 years
    This just returns the actual image src i.e. /images/theImage.png
  • Maximus
    Maximus over 5 years
    This worked brilliantly. I wanted to show images outside of the webroot and this is exactly what I was looking for. So, thanks!