Generate PDF and video thumbnails with VichUploader on Symfony
This will work with any VichUploader pluggable filesystem, like Gaufrette, OneupFlysystem, on my side, it’s an AWS S3 OneUp FS.
First of all, we need to understand VichUploaderBundle events especially when we have several UploadableField on the same Entity.
As we can see, files are uploaded one by one, so for 2 files in our entity, we will have 4 fired events (File1 preUpload, File1 postUpload, File2 preUpload, File2 postUpload ).
It is important to get that behavior, because once File 1 upload is done, his file is deleted.
In my case, File 2 will be the thumbnail of File 1 named file.
The Vich part of our Entity
/**
* NOTE: This is not a mapped field of entity metadata, just a simple property.
*
* @FileConstraint(
* mimeTypesMessage = "This file type is not supported",
* maxSize="1024M",
* binaryFormat=false
* )
*
* @Vich\UploadableField(mapping="customer_file", fileNameProperty="name", size="size", mimeType="mimeType", originalName="originalName")
*
* @var File
*/
private $file;
/**
* NOTE: This is not a mapped field of entity metadata, just a simple property.
*
* @FileConstraint(
* mimeTypesMessage = "This file type is not supported",
* maxSize="1024M",
* binaryFormat=false
* )
*
* @Vich\UploadableField(mapping="customer_file", fileNameProperty="thumbnailName")
*
* @var File
*/
private $thumbnail;
/**
* @Assert\Length(
* min = 1,
* max = 255,
* minMessage = "Your file name must be at least {{ limit }} characters long",
* maxMessage = "Your file name cannot be longer than {{ limit }} characters"
* )
*
* @ORM\Column(type="string", length=255)
*
* @var string
*/
private $name;
/**
* @Assert\Length(
* min = 1,
* max = 255,
* minMessage = "Your file name must be at least {{ limit }} characters long",
* maxMessage = "Your file name cannot be longer than {{ limit }} characters"
* )
*
* @ORM\Column(type="string", length=255, nullable=true)
*
* @var string
*/
private $thumbnailName;
In onVichPreUpload event listener function, we will be able to generate a thumbnail of a mimeType file: application/pdf with Imagick native PHP lib and any video/* with FFMpeg : https://github.com/PHP-FFMpeg/PHP-FFMpeg.
FFMpeg PHP library has system libraries requirements: the package ffmpeg (available on brew, apt-get, etc…) which will installs ffmpeg and ffprobre binaries.
Don’t forget to configure the event listener it in your configuration: https://github.com/dustin10/VichUploaderBundle/blob/master/Resources/doc/events.md
Below my FileUploadListener
<?php
namespace AppBundle\EventListener;
use AppBundle\Entity\FileEntity;
use AppBundle\Model\FileMimeType;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\File\File;
use Vich\UploaderBundle\Event\Event;
use FFMpeg\{FFMpeg, Coordinate};
class FileUploadListener
{
/**
* @inheritdoc
*
* Executed before file upload
* We use it to compute md5 file checksum and set thumbnail into entity
*/
public function onVichUploaderPreUpload(Event $event)
{
if($event->getMapping()->getFilePropertyName() === 'thumbnail') {
return;
}
$object = $event->getObject();
if ($object instanceof FileEntity) {
$object->setChecksum(md5_file($object->getFile()->getPathname()));
}
if ($object instanceof FileEntity && in_array($object->getFile()->getMimeType(), FileEntity::THUMBNAIL_MIMETYPES)) {
$format = sprintf('%s.png', $object->getFile()->getRealPath());
if($object->getFile()->getMimeType() === FileMimeType::PDF) {
$imagick = new \Imagick();
$imagick->readImage(sprintf('%s[0]', $object->getFile()->getRealPath()));
$imagick->writeImage($format);
} else {
$ffmpeg = FFMpeg::create(array(
'ffmpeg.binaries' => '/usr/bin/ffmpeg',
'ffprobe.binaries' => '/usr/bin/ffprobe'
));
$video = $ffmpeg->open($object->getFile()->getRealPath());
$frame = $video->frame(Coordinate\TimeCode::fromSeconds(FileEntity::THUMBNAIL_VIDEO_SECONDS));
$frame->save($format);
}
$file = new File($format);
$thumbnail = new UploadedFile($file->getPathname(), $file->getFilename(), $file->getMimeType(), $file->getSize());
$object->setThumbnail($thumbnail);
$object->setThumbnailName($file->getFilename());
}
}
}
As you see, we need to indicate ffmpeg and ffprobe binaries locations, to do so, type: whereis (or locate) phpmpeg to get your bin folders.
Hope this can help !