symfonyvichuploaderbundle

Displaying an image uploaded outside of the public directory in a Symfony API


I am working with Symfony as an API and a React frontend divided in two separate projects. I store user uploaded images inside a medias/ folder at the root of the Symfony project because some images are confidential (this is a business app) so they can't be simply put inside the public/ folder.

The upload part is fine but I can't figure out how to display the file in the React app even though I get the full link from the API using VichUploader's $this->storage->resolveUri(...) as stated in the API-Platform documentation.

I understand why it doesn't work, as it gives me something like www.domain.com/medias/images/123.png while the medias/ folder sits outside of the public/ directory.

I have no idea how to display those files. Every single documentation (Symfony, Vich...), guides and forum posts I've found are about displaying those images inside Twig or a webpacked javascript that is inside the Symfony project, while my javascript is in another different project.

Can anybody give me some pointers as to where to look for a solution?

As a reference, this is my VichUploader's configuration file:

vich_uploader:
    db_driver: orm

    mappings:
        machine_image:
            uri_prefix: /medias/images/machine
            upload_destination: '%kernel.project_dir%/medias/images/machine'
            namer: Vich\UploaderBundle\Naming\OrignameNamer

Solution

  • You'll have to create a controller method that gets the image from the filesystem and create a response with that image.

    In that method you can also add some checks if the user is allowed to see the image, but that depends on your situation. In some cases the image url isn't 'guessable' and that might be enough security.

    Something like this:

    use Symfony\Component\HttpFoundation\Response;
    use Symfony\Component\HttpFoundation\ResponseHeaderBag;
    use Symfony\Component\Routing\Annotation\Route;
    
    final class ImageController extends Controller
    {
        #[Route(path: 'image/{img}', name: 'image', methods: ['GET'])]
        public function image(string $img) : Response
        {
            // .. add some security checks here
    
            $filepath = $this->storage->resolveUri($img);
    
            $response = new Response(file_get_contents($filepath));
    
            $disposition = $response->headers->makeDisposition(ResponseHeaderBag::DISPOSITION_INLINE, $img);
    
            $response->headers->set('Content-Disposition', $disposition);
            $response->headers->set('Content-Type', 'image/png');
    
            return $response;
        }
    }
    

    (not tested, feel free to ask questions if it's not working)

    While I'm not a big fan of 'there's a library/app/bundle for this'-answers, I'd like to recommend LiipImagineBundle if you want to add some other features. You can create a thumbnail, add a watermark, add a webp support, etc.