Public and private Media of SonataMediaBundle uploaded in different folders (web and private)
Alright I'm almost there. My files are being uploaded to any folder I want, but I have the problem of getting them to be displayed.
The idea is to create a custom provider that has its own filesystem and adapter services. The problem was that I needed to pass the adapter service the folder in which I want the files to be uploaded. That adapter is passed to the filesystem which is then passed to the provider. The problem with SonataMediaBundle is that it doesn't let you modify the directory of the adapter, so you have to create your own and pass it all the way up to your provider.
Services:
sonata.media.provider.private: class: MedAppBundle\Services\PrivateFilesProvider tags: - { name: sonata.media.provider } arguments: [%private_provider_name%,@medapp_private.filesystem,@sonata.media.cdn.server,@sonata.media.generator.default,@sonata.media.thumbnail.format]medapp_private.filesystem: class: MedAppBundle\Services\PrivateFilesystem arguments: [@medapp_private.adapter]medapp_private.adapter: class: MedAppBundle\Services\PrivateFileAdapter arguments: [%private_filesystem_directory%,true]
parameters.yml
private_filesystem_directory: %kernel.root_dir%/Resources/mediaprivate_provider_name: sonata.media.provider.private
The custom adapter
use Sonata\MediaBundle\Filesystem\Local;class PrivateFileAdapter extends Local{}
The filesystem
use Gaufrette\Filesystem;class PrivateFilesystem extends Filesystem{}
And finally, the provider
use Application\Sonata\MediaBundle\Entity\Media;use Gaufrette\Filesystem;use Sonata\MediaBundle\CDN\CDNInterface;use Sonata\MediaBundle\Generator\GeneratorInterface;use Sonata\MediaBundle\Provider\FileProvider;use Sonata\MediaBundle\Thumbnail\ThumbnailInterface;class PrivateFilesProvider extends FileProvider{ public function getPrivateMedia(Media $media) { // $path = $this->generatePublicUrl($media, 'reference'); // $ppath = $this->generatePrivateUrl($media, "reference"); $content = $this->getReferenceFile($media)->getContent(); header('Content-Type: ' . $media->getContentType()); return 'data:' . $media->getContentType() . ';base64,' . base64_encode($content); }}
Then, add this provider to any of your sonata_media contexts. I've added it to the default context, for example:
sonata_media: db_driver: doctrine_orm default_context: default contexts: default: providers: - sonata.media.provider.dailymotion - sonata.media.provider.youtube - sonata.media.provider.image - sonata.media.provider.file - sonata.media.provider.vimeo - sonata.media.provider.private
To create a new Media in the private folder:
$media = new Media(); $media->setBinaryContent($file);$media->setContext('default');$media->setProviderName('sonata.media.provider.private');$this->em->persist($media);$this->em->flush();
$file is the file content that came from your form.This will create the folder app\Resources\media\default\0001\01
in which the files will be uploaded.
Now the problem is retrieving them, since they don't have a public link and need to be retrieved in a controller. I've created a getPrivateMedia() method in the provider, but it isn't working as intended.
EDIT:Noticed that the services could also be defined like so:
sonata.media.provider.private: class: MedAppBundle\Services\PrivateFilesProvider tags: - { name: sonata.media.provider } arguments: [%private_provider_name%,@medapp_private.filesystem,@sonata.media.cdn.server,@sonata.media.generator.default,@sonata.media.thumbnail.format,%kernel.root_dir%]medapp_private.filesystem: class: Gaufrette\Filesystem arguments: [@medapp_private.adapter]medapp_private.adapter: class: Sonata\MediaBundle\Filesystem\Local arguments: [%private_filesystem_directory%,true]
There is no need to create a custom filesystem or adapter for the provider, just create new services and pass the directory to the adapter, then pass the adapter to the filesystem and the filesystem to the custom provider. This is if you don't need any custom functionality.
I've had the exact same problem so thanks very much for the upload solution.
For downloading the files from the secured directory - sonata provides a method for that so you don't even need the custom provider as outlined here: https://sonata-project.org/bundles/media/master/doc/reference/security.html
My implementation of this looks like so:
services: sonata.media.provider.private: class: Sonata\MediaBundle\Provider\FileProvider tags: - { name: sonata.media.provider } arguments: - 'sonata.media.provider.private' - '@app.sonata.media.private.filesystem' - '@sonata.media.cdn.server' - '@sonata.media.generator.default' - '@sonata.media.thumbnail.format' app.sonata.media.private.filesystem: class: Gaufrette\Filesystem arguments: ['@app.sonata.media.private.adapter'] app.sonata.media.private.adapter: class: Sonata\MediaBundle\Filesystem\Local arguments: - "%kernel.root_dir%/../doclib/uploads" - true
Context with an explicitly defined security policy to control who gets to access the files. Super admin in this case:
sonata_media: contexts: privatefiles: download: strategy: sonata.media.security.superadmin_strategy mode: http providers: - sonata.media.provider.private
In twig you can then get links to the downloadable resource like so:
{{ path('sonata_media_download', { 'id':media|sonata_urlsafeid }) }}
sonata_media_download
maps MediaController::downloadAction()
, which implements the functionality you tried to achieve with getPrivateMedia()
.
The bit I am now stuck on is to set stuff like allowed_extensions
etc for the new provider as the providers
section won't accept private
as name so I assume this needs specifically defined somewhere.
EDIT: allowed_extensions
etc can be passed as further arguments via the custom provider service definition.