arxy / files
Installs: 6 496
Dependents: 0
Suggesters: 0
Security: 0
Stars: 17
Watchers: 3
Forks: 0
Open Issues: 1
Type:symfony-bundle
Requires
- php: ^8.0
- gabrielelana/byte-units: ^0.5.0
- league/mime-type-detection: ^1.7
Requires (Dev)
- aws/aws-sdk-php: *
- doctrine/doctrine-bundle: ^2.3
- doctrine/orm: *
- imagine/imagine: ^1.2
- infection/infection: ^0.21.4
- league/flysystem: ^2.0 | ^3.0
- league/flysystem-bundle: ^2.0
- league/flysystem-memory: ^2.0
- liip/imagine-bundle: ^2.6
- microsoft/azure-storage-blob: *
- phpunit/phpunit: ^9.5
- slevomat/coding-standard: ^7.0
- squizlabs/php_codesniffer: *
- symfony/asset: *
- symfony/cache: *
- symfony/dependency-injection: ^5.2
- symfony/form: *
- symfony/http-foundation: *
- symfony/http-kernel: *
- symfony/messenger: *
- symfony/symfony: ^4.4 | ^5.2
- symfony/uid: *
- symfony/validator: *
- twig/twig: *
- vimeo/psalm: ^4.7
Suggests
- aws/aws-sdk-php: Generate file urls for files hosted on AWS S3
- doctrine/orm: Use Doctrine ORM as persistence layer
- imagine/imagine: Use ImagePreviewGenerator
- league/flysystem: Use FlySystem Storage to store contents of file
- liip/imagine-bundle: Generate image thumbnails
- microsoft/azure-storage-blob: Generate file urls for files hosted on Microsoft Azure Blob Storage
- symfony/event-dispatcher: Usage of events.
- symfony/form: Integrate file upload with forms
- symfony/messenger: Generate Previews asynchronous.
- symfony/uid: Required to use UuidV5Strategy/UuidV4Strategy Naming Strategy
- symfony/validator: Validate files against constraints
- twig/twig: Use Arxy\Files\Twig\FilesExtension::formatBytes to get formatted KiB/MiB/GB instead. Also for form template
README
Provides easy file management (with persistence layer for metadata).
- Uses FlySystem for File management (this allows you to use existing adapters to save files anywhere)
- Persist file information in database
- Uses checksums (md5) to prevent double upload (thus saving space). If same file is found - it's reused
- Automatic hooks that manages the files (on entity persist file will be uploaded, on entity remove - file will be removed)
- Different naming strategies for handling files.
Usage
Configuring
Create the object which will holds the file
<?php namespace App\Entity; use Doctrine\ORM\Mapping as ORM; /** * @ORM\Entity() * @ORM\Table() */ class File extends \Arxy\FilesBundle\Entity\File { /** * @var int|null * @ORM\Id() * @ORM\Column(type="integer") * @ORM\GeneratedValue() */ protected $id; /** * @return int|null */ public function getId(): ?int { return $this->id; } /** * @param int|null $id */ public function setId(?int $id): void { $this->id = $id; } }
In case you want to use embeddable instead of Entity for file object:
<?php namespace App\Entity; use Arxy\FilesBundle\Model\File;use Doctrine\ORM\Mapping as ORM; use Arxy\FilesBundle\Entity\EmbeddableFile; /** * @ORM\Entity() * @ORM\Table() */ class News { /** @ORM\Embedded(class=EmbeddableFile::class) */ private ?EmbeddableFile $image = null; public function getImage(): ?File { return $this->image; } public function setImage(?File $file) { $this->image = $file; } }
Create the repository.
<?php namespace App\Repository; use Arxy\FilesBundle\Repository; use Doctrine\ORM\EntityRepository; use \Arxy\FilesBundle\Repository\ORM; class FileRepository extends EntityRepository implements Repository { use ORM; }
services: Arxy\FilesBundle\NamingStrategy\SplitHashStrategy: ~ flysystem: storages: in_memory: adapter: 'memory' arxy_files: managers: public: driver: orm class: 'App\Entity\File' storage: 'in_memory' naming_strategy: 'Arxy\FilesBundle\NamingStrategy\SplitHashStrategy' repository: 'App\Repository\FileRepository'
Or using plain services:
services: files_local_adapter: class: League\Flysystem\Local\LocalFilesystemAdapter arguments: - "/directory/for/files/" League\Flysystem\Filesystem: - "@files_local_adapter" League\Flysystem\FilesystemOperator: alias: League\Flysystem\Filesystem Arxy\FilesBundle\Twig\FilesExtension: tags: - { name: twig.extension } Arxy\FilesBundle\NamingStrategy\IdToPathStrategy: ~ Arxy\FilesBundle\NamingStrategy\AppendExtensionStrategy: - '@Arxy\FilesBundle\NamingStrategy\IdToPathStrategy' Arxy\FilesBundle\NamingStrategy: alias: Arxy\FilesBundle\NamingStrategy\AppendExtensionStrategy Arxy\FilesBundle\Storage\FlysystemStorage: ~ Arxy\FilesBundle\Storage: alias: 'Arxy\FilesBundle\Storage\FlysystemStorage' Arxy\FilesBundle\Manager: $class: 'App\Entity\File' Arxy\FilesBundle\ManagerInterface: alias: Arxy\FilesBundle\Manager Arxy\FilesBundle\EventListener\DoctrineORMListener: arguments: [ "@Arxy\\FilesBundle\\ManagerInterface" ] # This can be omit, if using autowiring. tags: - { name: doctrine.event_listener, event: 'postPersist' } - { name: doctrine.event_listener, event: 'preRemove' } Arxy\FilesBundle\Form\Type\FileType: arguments: [ "@Arxy\\FilesBundle\\ManagerInterface" ] # This can be omit, if using autowiring. tags: # This can be omit, if using autowiring. - { name: form.type }
or using pure PHP
$adapter = new \League\Flysystem\Local\LocalFilesystemAdapter; $filesystem = new \League\Flysystem\Filesystem($adapter); $storage = new \Arxy\FilesBundle\Storage\FlysystemStorage($filesystem); $namingStrategy = new \Arxy\FilesBundle\NamingStrategy\SplitHashStrategy(); $repository = new FileRepository(); $fileManager = new \Arxy\FilesBundle\Manager(\App\Entity\File::class, $storage, $namingStrategy, $repository);
Upload file
$file = new \SplFileInfo($pathname); $fileEntity = $fileManager->upload($file); $file = $request->files->get('file'); $fileEntity = $fileManager->upload($file); $entityManager->persist($fileEntity); $entityManager->flush();
In case of embeddable:
$file = new \SplFileInfo($pathname); $embeddableFile = $fileManager->upload($file); $news = new \App\Entity\News(); $news->setImage($embeddableFile); $entityManager->persist($news); $entityManager->flush();
Please note that file is not actually moved to its final location until file is persisted into db, which is done by Listeners. (Arxy\FilesBundle\DoctrineORMListener for example)
Upload using form Type:
$formBuilder->add( 'image', FileType::class, [ 'required' => false, 'constraints' => [ConstraintsOnEntity] 'input_options' => [ 'attr' => [ 'accept' => 'image/*', ], 'constraints' => [ SymfonyConstraintsOnFiles ] ], ] );
Read file content
$file = $entityManager->find(File::class, 1); $content = $fileManager->read($file);
Read stream
$file = $entityManager->find(File::class, 1); $fileHandle = $fileManager->readStream($file);
This bundle also contains form and constraint for uploading and validating files. You can write your own naming strategy how files are created on Filesystem. You can even write your own FileSystem backend for Flysystem and use it here.
Currently, only Doctrine ORM is supported as persistence layer. Feel free to submit PRs for others.
Serving files from controller
- Serving with Controller
Create the controller which will serve files.
<?php declare(strict_types=1); namespace App\Controller; use App\Entity\File; use Doctrine\ORM\EntityManagerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\Routing\Annotation\Route; use Arxy\FilesBundle\Utility\DownloadUtility; use Symfony\Component\HttpFoundation\Response; class FileController extends AbstractController { /** * @Route(path="/file/{id}", name="file_download") */ public function download( string $id, EntityManagerInterface $em, DownloadUtility $downloadUtility ): Response { $file = $em->getRepository(File::class)->findOneBy( [ 'md5Hash' => $id, ] ); if ($file === null) { throw $this->createNotFoundException('File not found'); } return $downloadUtility->createResponse($file); } }
If you want to force different download name, you can decorate file with Arxy\FilesBundle\Utility\DownloadableFile
:
<?php declare(strict_types=1); namespace App\Controller; use App\Entity\File; use Doctrine\ORM\EntityManagerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\Routing\Annotation\Route; use Arxy\FilesBundle\Utility\DownloadUtility; use Arxy\FilesBundle\Utility\DownloadableFile; use Symfony\Component\HttpFoundation\Response; class FileController extends AbstractController { /** * @Route(path="/file/{id}", name="file_download") */ public function download( string $id, EntityManagerInterface $em, DownloadUtility $downloadUtility ): Response { $file = $em->getRepository(File::class)->findOneBy( [ 'md5Hash' => $id, ] ); if ($file === null) { throw $this->createNotFoundException('File not found'); } return $downloadUtility->createResponse(new DownloadableFile($file, 'my_name.jpg', false, new \DateTimeImmutable('date of cache expiry'))); } }
Serving
You might want to use LiipImagineBundle or CDN solution, or even controller.
Directly
If you want directly to serve file with CDN, you can use Path Resolver + Normalizer:
<?php declare(strict_types=1); namespace App\Serializer; use App\Entity\File; use Arxy\FilesBundle\PathResolver; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; class FileNormalizer implements NormalizerInterface { private ObjectNormalizer $objectNormalizer; private PathResolver $pathResolver; public function __construct( ObjectNormalizer $objectNormalizer, PathResolver $pathResolver ) { $this->objectNormalizer = $objectNormalizer; $this->pathResolver = $pathResolver; } public function normalize($object, $format = null, array $context = array()) { assert($object instanceof File); $data = $this->objectNormalizer->normalize($object, $format, $context); $data['url'] = $this->pathResolver->getPath($object); return $data; } public function supportsNormalization($data, $format = null) { return $data instanceof File; } }
/** * @var string * @Groups({"file_read"}) */ private string $url = null; public function getUrl(): array { return $this->url; } public function setUrl(string $url): void { $this->url = $url; }
You will receive following json as response:
{ "id": 145, "mimeType": "application/pdf", "size": 532423, "url": "https://example.com/link-to-image.pdf" }
LiipImagineBundle
If you want to use it with LiipImagineBundle, you probably could add something like that:
/** * @var array * @Groups({"file_read"}) */ private $formats = []; public function getFormats(): array { return $this->formats; } public function setFormats(array $formats): void { $this->formats = $formats; }
and fill these from Event Listener or ORM Listener or Serializer Normalizer.
here is example with Serializer Normalizer:
<?php declare(strict_types=1); namespace App\Serializer; use App\Entity\File; use Arxy\FilesBundle\LiipImagine\FileFilter;use Arxy\FilesBundle\LiipImagine\FileFilterPathResolver;use Liip\ImagineBundle\Imagine\Filter\FilterConfiguration; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; class FileNormalizer implements NormalizerInterface { private ObjectNormalizer $objectNormalizer; private FileFilterPathResolver $fileFilterPathResolver; private FilterConfiguration $filterConfiguration; public function __construct( ObjectNormalizer $objectNormalizer, FileFilterPathResolver $fileFilterPathResolver, FilterConfiguration $filterConfiguration ) { $this->objectNormalizer = $objectNormalizer; $this->fileFilterPathResolver = $fileFilterPathResolver; $this->filterConfiguration = $filterConfiguration; } public function normalize($object, $format = null, array $context = array()) { assert($object instanceof \Arxy\FilesBundle\Model\File); $data = $this->objectNormalizer->normalize($object, $format, $context); $data['formats'] = array_reduce( array_keys($this->filterConfiguration->all()), function ($array, $filter) use ($object) { $array[$filter] = $this->fileFilterPathResolver->getUrl(new FileFilter($object, $filter)); return $array; }, [] ); return $data; } public function supportsNormalization($data, $format = null) { return $data instanceof \Arxy\FilesBundle\Model\File; } }
You will receive following json as response:
{ "id": 145, "formats": { "squared_thumbnail": "https:\/\/host.com\/media\/cache\/resolve\/squared_thumbnail\/1\/4\/5\/145" } }
Naming Strategies:
Naming strategy is responsible to converting File object to filepath. Several built-in strategies exists:
DateStrategy
Use file's createdAt property. Default format: Y/m/d/hash. Example: 2021/05/17/59aeac36ae75786be1b573baad0e77c0
SplitHashStrategy
Use file's md5hash and split it into chucks. Example: 098f6bcd4621d373cade4e832627b4f6
will result
in 098f6bcd/4621d373/cade4e83/2627b4f6/098f6bcd4621d373cade4e832627b4f6
UuidV5Strategy
Uses UUID V5 to generate hash for file. It consists of namespace (configurable) and value (Uses md5Hash of file).
AppendExtensionStrategy
Decorator which adds extension of file (.jpg, .pdf, etc).
DirectoryPrefixStrategy
Decorator which prefixes the generated directory of another naming strategy.
NullDirectoryStrategy
Decorator which always return null directory.
PersistentPathStrategy
Use persisted pathname in file. Useful if you want to generate completely random path for each file. (For example UUID
v4) or you just want the path to the file persisted for some reason. Expects instanceof
Arxy\FilesBundle\Model\PathAwareFile
. It's your responsibility to handle the path itself. You can do that with custom
Arxy\FilesBundle\ModelFactory
(recommended) or use built in Event Listener, which will set the pathname on
upload (Arxy\FilesBundle\EventListener\PathAwareListener
)
UUID V4 Strategy:
Generates random path.
Migrating between naming strategy.
Register Migrator service and command:
services: Arxy\FilesBundle\Migrator: $filesystem: '@League\Flysystem\FilesystemOperator' $oldNamingStrategy: '@old_naming_strategy' $newNamingStrategy: '@new_naming_strategy' Arxy\FilesBundle\Command\MigrateNamingStrategyCommand: $migrator: '@Arxy\FilesBundle\Migrator' $repository: '@repository'
then run it.
bin/console arxy:files:migrate-naming-strategy
PathResolver: used to generate browser URL to access the file. Few built-in resolvers exists:
AssetsPathResolver:
Arxy\FilesBundle\PathResolver\AssetsPathResolver: $manager: '@Arxy\FilesBundle\ManagerInterface' $package: 'packageName' # https://symfony.com/doc/current/components/asset.html#asset-packages Arxy\FilesBundle\PathResolver: alias: Arxy\FilesBundle\PathResolver\AssetsPathResolver
AwsS3PathResolver:
Aws\S3\S3Client: class: Aws\S3\S3Client arguments: $args: region: 'region' version: 'version' credentials: key: 'key' secret: 'secret' Aws\S3\S3ClientInterface: alias: Aws\S3\S3Client Arxy\FilesBundle\PathResolver\AwsS3PathResolver: arguments: $s3Client: '@Aws\S3\S3ClientInterface' $bucket: 'bucket-name' $manager: '@Arxy\FilesBundle\ManagerInterface' Arxy\FilesBundle\PathResolver: alias: Arxy\FilesBundle\PathResolver\AwsS3PathResolver
AzureBlobStoragePathResolver:
MicrosoftAzure\Storage\Blob\BlobRestProxy: factory: [ 'MicrosoftAzure\Storage\Blob\BlobRestProxy', 'createBlobService' ] arguments: $connectionString: 'DefaultEndpointsProtocol=https;AccountName=xxxxxxxx;EndpointSuffix=core.windows.net' Arxy\FilesBundle\PathResolver\AzureBlobStoragePathResolver: arguments: $client: '@MicrosoftAzure\Storage\Blob\BlobRestProxy' $container: 'container-name' $manager: '@Arxy\FilesBundle\ManagerInterface' Arxy\FilesBundle\PathResolver: alias: Arxy\FilesBundle\PathResolver\AzureBlobStoragePathResolver
AzureBlobStorageSASPathResolver:
- Decorator that accepts
Arxy\FilesBundle\PathResolver\AzureBlobStoragePathResolver
and adds SAS Signature
Create AzureBlobStorageSASParametersFactory
instance that will be responsible for creating parameters for signature.
class MyFactory implements \Arxy\FilesBundle\PathResolver\AzureBlobStorageSASParametersFactory { public function create(\Arxy\FilesBundle\Model\File $file) : \Arxy\FilesBundle\PathResolver\AzureBlobStorageSASParameters { return new \Arxy\FilesBundle\PathResolver\AzureBlobStorageSASParameters( new \DateTimeImmutable('+10 minutes'), ); } }
MicrosoftAzure\Storage\Blob\BlobSharedAccessSignatureHelper: arguments: $accountName: 'account-name' $accountKey: 'account-key' MyFactory: ~ Arxy\FilesBundle\PathResolver\AzureBlobStorageSASParametersFactory: alias: '@MyFactory' Arxy\FilesBundle\PathResolver\AzureBlobStorageSASPathResolver: arguments: $pathResolver: '@Arxy\FilesBundle\PathResolver\AzureBlobStoragePathResolver' $signatureHelper: '@MicrosoftAzure\Storage\Blob\BlobSharedAccessSignatureHelper' $factory: '@Arxy\FilesBundle\PathResolver\AzureBlobStorageSASParametersFactory' Arxy\FilesBundle\PathResolver: alias: Arxy\FilesBundle\PathResolver\AzureBlobStorageSASPathResolver
CachePathResolver:
Used to cache the result from decorated Path Resolver. Useful for example in conjunction with AwsS3PathResolver, where to get the path to uploaded file, an API call is made. This resolver will cache the response from AWS S3 servers and next time you need the file path, it will be returned from cache. Uses https://symfony.com/doc/current/components/cache.html
Arxy\FilesBundle\PathResolver\AwsS3PathResolver: arguments: $bucket: '%env(AWS_S3_BUCKET)%' $manager: '@Arxy\FilesBundle\ManagerInterface' Arxy\FilesBundle\PathResolver\CachePathResolver: arguments: $pathResolver: '@Arxy\FilesBundle\PathResolver\AwsS3PathResolver' $cache: '@cache.app' Arxy\FilesBundle\PathResolver: alias: Arxy\FilesBundle\PathResolver\CachePathResolver
DelegatingPathResolver:
Used when your system have multiple file entities:
Arxy\FilesBundle\PathResolver\DelegatingPathResolver: $resolvers: 'App\Entity\File': '@path_resolver' 'App\Entity\OtherFile': '@other_path_resolver'
You can also combine Manager and PathResolver into one, using PathResolverManager decorator, so you can use singe instance for both operations:
Arxy\FilesBundle\PathResolverManager: $manager: '@manager' $pathResolver: '@path_resolver' Arxy\FilesBundle\ManagerInterface: alias: Arxy\FilesBundle\PathResolverManager Arxy\FilesBundle\PathResolver: alias: Arxy\FilesBundle\PathResolverManager
There is also DelegatingManager, which can be used as router to different other managers, supporting different classes.
Arxy\FilesBundle\DelegatingManager: $managers: [ '@manager_1', '@manager_2' ]
Then you can do: $manager->getManagerFor(File::class)->upload($file)
. Note: If you do
directly $manager->upload($file)
- it will call first manager's upload method. Reading is even easier: Just pass
the $file
directly, it will determine the correct inner manager for that file.
$manager->read($file)
Sending additional parameters to path resolver.
Ok, we have path generated to our files now, but what if we want to represent same file, differently? Obviously we cannot do this currently. Let's change that! Let's say we need to enforce different download name.
- We start by creating decorated file.
class VirtualFile extends \Arxy\FilesBundle\Model\DecoratedFile { private ?string $downloadFilename = null; public function setDownloadFilename(string $filename) { $this->downloadFilename = $filename; } public function getDownloadFilename(): ?string { return $this->downloadFilename; } }
- Then we create/decorate also the path resolver
class VirtualFilePathResolver implements \Arxy\FilesBundle\PathResolver { public function getPath(\Arxy\FilesBundle\Model\File $file): string { assert($file instanceof VirtualFile); return sprintf('url?download_filename=%s', $file->getDownloadFilename()); } }
- Then we can use path resolver as usual:
public function someAction(\Arxy\FilesBundle\PathResolver $pathResolver) { $virtualFile = new \Arxy\FilesBundle\Tests\VirtualFile($file); $virtualFile->setDownloadFilename('this_file_is_renamed_during_download.jpg'); $downloadUrl = $pathResolver->getPath($virtualFile); }
Twig Extensions:
- Arxy\FilesBundle\Twig\FilesExtensions:
int 12345|format_bytes(int $precision = 2)
- format bytes as kb,mb, etc.Arxy\FilesBundle\Model\File $file|file_content
- return the contents of file.
- Arxy\FilesBundle\Twig\PathResolverExtension:
file_path(Arxy\FilesBundle\Model\File $file)
- return downloadable path for file using path resolver.
LiipImagine:
If you need to generate thumbnails for your files, you could use built-in integration with LiipImagineBundle:
- Setup LiipImagineBundle.
- Register
Arxy\FilesBundle\LiipImagine\FileFilterPathResolver
as service. - Use service from point2 as follows:
$pathResolver->getPath(new \Arxy\FilesBundle\LiipImagine\FileFilter($file, 'filterName'));
Usage with API Platform:
Uploading
<?php declare(strict_types=1); namespace App\Controller\ApiPlatform; use Arxy\FilesBundle\Manager; use Arxy\FilesBundle\Model\File; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; final class Upload { private Manager $fileManager; public function __construct(Manager $fileManager) { $this->fileManager = $fileManager; } public function __invoke(Request $request): File { $uploadedFile = $request->files->get('file'); if (!$uploadedFile) { throw new BadRequestHttpException('"file" is required'); } return $this->fileManager->upload($uploadedFile); } }
<?php declare(strict_types=1); namespace App\Entity; use ApiPlatform\Core\Annotation\ApiResource; use App\Controller\ApiPlatform\Upload; use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Serializer\Annotation\Groups; /** * @ORM\Entity * @ORM\Table(name="files") * @ApiResource( * iri="http://schema.org/MediaObject", * normalizationContext={ * "groups"={"file_read"} * }, * collectionOperations={ * "post"={ * "controller"=Upload::class, * "deserialize"=false, * "validation_groups"={"Default"}, * "openapi_context"={ * "requestBody"={ * "content"={ * "multipart/form-data"={ * "schema"={ * "type"="object", * "properties"={ * "file"={ * "type"="string", * "format"="binary" * } * } * } * } * } * } * } * } * }, * itemOperations={ * "get" * } * ) */ class File extends \Arxy\FilesBundle\Entity\File { /** * @var int|null * * @ORM\Id * @ORM\Column(type="integer", nullable=false) * @ORM\GeneratedValue * @Groups({"file_read"}) */ protected ?int $id = null; public function getId(): ?int { return $this->id; } }
Events
PostUpload
Arxy\FilesBundle\Events\PostUpload
event is called right after File object is created. It is NOT called if existing
file is found and re-used. At this moment file is located on local FS.
PreMove
Arxy\FilesBundle\Events\PreMove
event is called right before File object is moved into its final location. At this
moment file is still located locally. so ManagerInterface::getPathname()
returns local filepath.
PostMove
Arxy\FilesBundle\Events\PostMove
event is called right after File object is moved into its final location. At this
moment file is located in FlySystem. so ManagerInterface::getPathname()
returns filepath generated from naming
strategy.
PreUpdate
Arxy\FilesBundle\Events\PreUpdate
event is called right before File object is updated through write, writeStream.
PostUpdate
Arxy\FilesBundle\Events\PostUpdate
event is called right after File object is updated through write, writeStream.
PreRemove
Arxy\FilesBundle\Events\PreRemove
event is called right before file is deleted from filesystem.
Preview
There is a sub-system for preview generation for files: It generates preview and saves it as another file. There are 2 ways to enable it:
-
Synchronous generation:
Arxy\FilesBundle\Preview\PreviewGeneratorListener
-
Asynchronous generation using Symfony Messenger:
Arxy\FilesBundle\Preview\GeneratePreviewMessageHandler
Arxy\FilesBundle\Preview\PreviewGeneratorMessengerListener
And then register common services:
Imagine\Gd\Imagine: ~ Arxy\FilesBundle\Preview\ImagePreviewGenerator: $manager: '@public' <-- manager of files. $imagine: '@Imagine\Gd\Imagine' Arxy\FilesBundle\Preview\Dimension: $width: 250 <- width of generated thumbnail $height: 250 <- height of generated thumbnail Arxy\FilesBundle\Preview\DimensionInterface: '@Arxy\FilesBundle\Preview\Dimension' Arxy\FilesBundle\Preview\PreviewGenerator: $manager: '@preview' <- manager of previews $generators: - '@Arxy\FilesBundle\Preview\ImagePreviewGenerator'
Currently, only image preview generator exists. You can add your own image preview generator. Just implement the
Arxy\FilesBundle\Preview\PreviewGeneratorInterface
.
Known issues
- If file entity is deleted within transaction and transaction is rolled back - file will be deleted. I'm waiting for DBAL 3.2.* release to be able to fix that.