I'm using SonataMediaBundle and until now I've been uploading files publicly in the web
folder of Symfony. Here is part of my configuration file:
contexts:
default: # the default context is mandatory
providers:
- sonata.media.provider.dailymotion
- sonata.media.provider.youtube
- sonata.media.provider.image
- sonata.media.provider.file
- sonata.media.provider.vimeo
formats:
small: { width: 100 , quality: 70}
big: { width: 500 , quality: 70}
profile_pics: # the default context is mandatory
providers:
- sonata.media.provider.dailymotion
- sonata.media.provider.youtube
- sonata.media.provider.image
- sonata.media.provider.file
- sonata.media.provider.vimeo
formats:
small: { width: 100 , quality: 70}
big: { width: 500 , quality: 70}
cdn:
server:
path: %cdn_server_path% # http://media.sonata-project.org/
filesystem:
local:
directory: %kernel.root_dir%/../web/uploads/media
create: false
And in my parameters.yml
I have cdn_server_path: /uploads/media/
.
This will upload files in the web/uploads/media/contextname/0001/01/***
.
Being in the public folder, these files are visible to everyone. I know I can change this folder somewhere in app/Resources/
, but that will make ALL of my files private, which I don't want. By private, I mean that they should be served by a controller, somehow.
How can I separate some files being public and others being private? Is there a way to make a certain context upload files differently than another? Or any other way?
Also, when uploading files to the app/Resources folder, they cannot be previewed since their url becomes http://website.com/app/Resources/media/profile_pics/0001/01/85e05ab9685b1745af4e64ff98ef91eed6e4ccdf.jpeg which is not public, because it's not in the web folder, the only public files that Symfony has. How can this be done?
EDIT: I've managed to do the uploading part(check the answer below), but the retrieving part is a bit troublesome. I'm still looking for a way to do it.
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/media
private_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.