Использовать LiipImagineBundle для изменения размера изображения после загрузки?
Я использую https://github.com/liip/LiipImagineBundle с Symfony 2.1 и хотел бы изменить размер загруженных пользователем изображений при загрузке, прежде чем сохранять их в постоянном месте файловой системы (для удаления метаданных, наложения формата jpeg и ограничения размера файла). Я должен вызвать фильтр 'strip' и 'resize' из контроллера, а затем сохранить отфильтрованное изображение из временного расположения в папку по своему выбору в файловой системе.
Я пытался использовать контроллер LiipImageBundle в качестве службы, как указано в readme пакета, но вызываемое действие в основном предназначено для создания отфильтрованного изображения в каталоге кэша, когда делается запрос на отображение изображения (другой случай - использование его для фильтрации во время загрузки).). В любом случае я попытался реализовать это следующим образом и заставил работать. Мне пришлось сначала переместить файл из временного каталога php веб-сервера в каталог в веб-папке, чтобы иметь возможность применить фильтр. Во-вторых, я применил фильтр и удалил (unlink()) исходный нефильтрованный файл. Наконец, мне пришлось переместить (rename()) отфильтрованный файл в постоянное место в файловой системе. Необходимо было дважды переместить файл, применить фильтр один раз и удалить (отсоединить) 1 файл, чтобы все это работало. Есть ли лучший способ (не требующий промежуточного перемещения) использовать пакет при загрузке?
class MyController extends Controller
{
public function new_imageAction(Request $request)
{
$uploadedFile = $request->files->get('file');
$tmpFolderPathAbs = $this->get('kernel')->getRootDir() . '/../web/uploads/tmp/';
$tmpImageNameNoExt = rand();
$tmpImageName = $tmpImageNameNoExt . '.' . $fileExtension;
$uploadedFile->move($tmpFolderPathAbs, $tmpImageName);
$tmpImagePathRel = '/uploads/tmp/' . $tmpImageName;
// Create the filtered image in a tmp folder:
$this->container->get('liip_imagine.controller')->filterAction($request, $tmpImagePathRel, 'my_filter');
unlink($tmpFolderPathAbs . $tmpImageName);
$filteredImagePathAbs = $this->get('kernel')->getRootDir() . '/../web/uploads/cache/my_filter/uploads/tmp/' . $tmpImageNameNoExt . '.jpeg';
$imagePath = $imageManagerResponse->headers->get('location');
// define permanent location ($permanentImagePathAbs)...
rename($filteredImagePathAbs, $permanentImagePathAbs);
}
}
Мой фильтр в app/config/config.yml выглядит следующим образом:
liip_imagine:
filter_sets:
my_filter:
format: jpeg
filters:
strip: ~
thumbnail: { size: [1600, 1000], mode: inset }
Аналогичный вопрос был задан для ImagineAvalancheBundle, но подробностей не приводится. Возможно, реализация другого сервиса из представленного здесь списка является лучшим решением?
5 ответов
Итак, вот способ создания миниатюр при загрузке с помощью LiipImagineBundle. Хитрость заключается в том, чтобы использовать некоторые из их других сервисов:
/**
* Write a thumbnail image using the LiipImagineBundle
*
* @param Document $document an Entity that represents an image in the database
* @param string $filter the Imagine filter to use
*/
private function writeThumbnail($document, $filter) {
$path = $document->getWebPath(); // domain relative path to full sized image
$tpath = $document->getRootDir().$document->getThumbPath(); // absolute path of saved thumbnail
$container = $this->container; // the DI container
$dataManager = $container->get('liip_imagine.data.manager'); // the data manager service
$filterManager = $container->get('liip_imagine.filter.manager');// the filter manager service
$image = $dataManager->find($filter, $path); // find the image and determine its type
$response = $filterManager->get($this->getRequest(), $filter, $image, $path); // run the filter
$thumb = $response->getContent(); // get the image from the response
$f = fopen($tpath, 'w'); // create thumbnail file
fwrite($f, $thumb); // write the thumbnail
fclose($f); // close the file
}
Это также может быть сделано путем прямого вызова функций библиотеки Imagine, если у вас не было другой причины для включения LiipImagineBundle. Я, вероятно, рассмотрю это в будущем, но это работает для моего случая и работает очень хорошо.
Модифицированная версия @Peter Wooster, сделавшая ее более универсальной, так что если кто-то использует ее без сущности Image, он / она может легко извлечь из нее пользу. Я даю здесь две версии, одну из которых можно использовать в служебном или неконтроллерном классе. А другая версия для классов контроллеров. Тебе решать, где тебе нравится:)
Использовать вне контроллера, например, сохраняя его в служебных классах
/**
* Write a thumbnail image using the LiipImagineBundle
*
* @param Document $fullSizeImgWebPath path where full size upload is stored e.g. uploads/attachments
* @param string $thumbAbsPath full absolute path to attachment directory e.g. /var/www/project1/images/thumbs/
* @param string $filter filter defined in config e.g. my_thumb
* @param Object $diContainer Dependency Injection Object, if calling from controller just pass $this
*/
public function writeThumbnail($fullSizeImgWebPath, $thumbAbsPath, $filter, $diContainer) {
$container = $diContainer; // the DI container, if keeping this function in controller just use $container = $this
$dataManager = $container->get('liip_imagine.data.manager'); // the data manager service
$filterManager = $container->get('liip_imagine.filter.manager'); // the filter manager service
$image = $dataManager->find($filter, $fullSizeImgWebPath); // find the image and determine its type
$response = $filterManager->applyFilter($image, $filter);
$thumb = $response->getContent(); // get the image from the response
$f = fopen($thumbAbsPath, 'w'); // create thumbnail file
fwrite($f, $thumb); // write the thumbnail
fclose($f); // close the file
}
Для использования в контроллере, например, CommonController или любом другом контроллере.
/**
* Write a thumbnail image using the LiipImagineBundle
*
* @param Document $fullSizeImgWebPath path where full size upload is stored e.g. uploads/attachments
* @param string $thumbAbsPath full absolute path to attachment directory e.g. /var/www/project1/images/thumbs/
* @param string $filter filter defined in config e.g. my_thumb
*/
public function writeThumbnail($fullSizeImgWebPath, $thumbAbsPath, $filter) {
$container = $this->container;
$dataManager = $container->get('liip_imagine.data.manager'); // the data manager service
$filterManager = $container->get('liip_imagine.filter.manager'); // the filter manager service
$image = $dataManager->find($filter, $fullSizeImgWebPath); // find the image and determine its type
$response = $filterManager->applyFilter($image, $filter);
$thumb = $response->getContent(); // get the image from the response
$f = fopen($thumbAbsPath, 'w'); // create thumbnail file
fwrite($f, $thumb); // write the thumbnail
fclose($f); // close the file
}
Вместо загрузки файла с помощью менеджера данных liip создайте двоичный объект liip из загруженного файла:
use Liip\ImagineBundle\Model\Binary;
затем используйте следующий код:
// Generate a unique name for the file before saving it
$fileName = md5(uniqid()) . '.' . $uploadedFile->guessExtension();
$contents = file_get_contents($uploadedFile);
$binary = new Binary(
$contents,
$uploadedFile->getMimeType(),
$uploadedFile->guessExtension()
);
$container = $this->container;
$filterManager = $container->get('liip_imagine.filter.manager'); // the filter manager service
$response = $filterManager->applyFilter($binary, 'my_thumb');
$thumb = $response->getContent(); // get the image from the response
$f = fopen($webRootDir .'/images_dir/' . $fileName, 'w'); // create thumbnail file
fwrite($f, $thumb); // write the thumbnail
fclose($f); // close the file
Я написал пакет, который точно решает эту проблему. В то время как VichUploaderBundle
обеспечивает простую загрузку с помощью обратных вызовов жизненного цикла ORM, LiipImagine
делает большую работу по изменению размера.
Вот комбинация этого: https://github.com/RSSfeed/VichImagineBundle
Прочтите краткое руководство по его внедрению всего за несколько минут.
Учитывая, что я не нашел лучшего способа, я сохранил решение, описанное в описании вопроса. Это решение не кажется оптимальным с точки зрения производительности (для этого необходимо дважды переместить файл, применить фильтр один раз и отсоединить файл 1), но оно выполняет свою работу.
ОБНОВИТЬ:
Я изменил свой код, чтобы использовать службы, указанные в ответе Питера Вустера, как показано ниже (это решение более оптимально, поскольку отфильтрованное изображение сохраняется непосредственно в конечный пункт назначения):
class MyController extends Controller
{
public function new_imageAction(Request $request)
{
$uploadedFile = $request->files->get('file');
// ...get file extension and do other validation...
$tmpFolderPathAbs = $this->get('kernel')->getRootDir() . '/../web/uploads/tmp/'; // folder to store unfiltered temp file
$tmpImageNameNoExt = rand();
$tmpImageName = $tmpImageNameNoExt . '.' . $fileExtension;
$uploadedFile->move($tmpFolderPathAbs, $tmpImageName);
$tmpImagePathRel = '/uploads/tmp/' . $tmpImageName;
// Create the filtered image:
$processedImage = $this->container->get('liip_imagine.data.manager')->find('my_filter', $tmpImagePathRel);
$filteredImage = $this->container->get('liip_imagine.filter.manager')->get($request, 'my_filter', $processedImage, $tmpImagePathRel)->getContent();
unlink($tmpFolderPathAbs . $tmpImageName); // eliminate unfiltered temp file.
$permanentFolderPath = $this->get('kernel')->getRootDir() . '/../web/uploads/path_to_folder/';
$permanentImagePath = $permanentFolderPath . 'my_image.jpeg';
$f = fopen($permanentImagePath, 'w');
fwrite($f, $filteredImage);
fclose($f);
}
}