1

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
yurden
  • 152
  • 1
  • 16
  • You should create a controller that 1) checks if the user is allowed to see the file, 2) get the file from the filesystem and 3) return it as a Response. As an alternative you could take a look at LiipImagineBundle so you can return an optimized version of the image (thumbnail, webp, etc.) – Stephan Vierkant Jul 30 '21 at 09:52
  • Right, so I have to make an extra request to the API just for the image then. I was hoping I could just get a working link from the API with the rest of the data. Could you rewrite your comment as an answer so I can validate it if no one comes up with a better solution? – yurden Jul 30 '21 at 10:11
  • You don't need to make another request to your API. Just get the file name and get the file. – Stephan Vierkant Jul 30 '21 at 13:33
  • What do you mean ? I'm getting my data through API-Platform so I don't have a controller for that call. I currently only resolve the full image path post load in an entity listener so I can have it among the rest of the data when I call my route. I could put my whole request inside a custom controller but then I feel like that defeats the purpose of using API-Platform – yurden Jul 30 '21 at 15:03
  • You have a React app that calls `/api/some-endpoint`. The result contains the file name of your image, right? The let your app call `/image/{img-file-name}`. That call (from the controller method from my answer) will get the file and returns it. – Stephan Vierkant Jul 30 '21 at 15:08
  • 1
    Ah yes I get it! We were saying the same thing from the start I think, I count that as another request to the API while you do not, that's all. Thanks for the help! – yurden Jul 30 '21 at 15:13

1 Answers1

1

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.

Stephan Vierkant
  • 9,674
  • 8
  • 61
  • 97