ImageController.php (5932B)
1 <?php 2 3 namespace PartKeepr\ImageBundle\Controller; 4 5 use Doctrine\ORM\EntityManager; 6 use Gaufrette\Exception\FileNotFound; 7 use Imagine\Gd\Imagine; 8 use Imagine\Image\Box; 9 use Nelmio\ApiDocBundle\Annotation\ApiDoc; 10 use PartKeepr\ImageBundle\Entity\CachedImage; 11 use PartKeepr\ImageBundle\Entity\Image as PartKeeprImage; 12 use PartKeepr\ImageBundle\Response\ImageResponse; 13 use PartKeepr\UploadedFileBundle\Controller\FileController; 14 use PartKeepr\UploadedFileBundle\Entity\UploadedFile; 15 use Symfony\Component\HttpFoundation\Request; 16 use Symfony\Component\HttpFoundation\Response; 17 18 abstract class ImageController extends FileController 19 { 20 /** 21 * @ApiDoc( 22 * description="Returns a scaled and cached image. Note that the binary data is directly returned.", 23 * parameters={ 24 * {"name"="maxWidth", "dataType"="integer", "required"=false, "description"="The width in pixels"}, 25 * {"name"="maxHeight", "dataType"="integer", "required"=false, "description"="The height in pixels"} 26 * } 27 * ) 28 * 29 * @param Request $request 30 * @param $id 31 * 32 * @return ImageResponse|Response 33 */ 34 public function getImageAction(Request $request, $id) 35 { 36 /** 37 * @var EntityManager 38 */ 39 $em = $this->getDoctrine()->getManager(); 40 41 /** 42 * @var PartKeeprImage 43 */ 44 $image = $em->find($this->getEntityClass(), $id); 45 46 $width = $request->get('maxWidth'); 47 $height = $request->get('maxHeight'); 48 49 if ($width === null) { 50 $width = 200; 51 } 52 53 if ($height === null) { 54 $height = 200; 55 } 56 57 if ($image === null) { 58 return new ImageResponse($width, $height, 404, '404 not found'); 59 } 60 61 try { 62 $file = $this->fitWithin($image, $width, $height); 63 } catch (FileNotFound $e) { 64 $this->get('logger')->error($e->getMessage()); 65 66 return new ImageResponse($width, $height, 404, '404 not found'); 67 } catch (\Exception $e) { 68 $this->get('logger')->error($e->getMessage()); 69 70 return new ImageResponse($width, $height, 500, '500 Server Error'); 71 } 72 73 return new Response(file_get_contents($file), 200, ['Content-Type' => 'image/png']); 74 } 75 76 /** 77 * Returns the path to the image cache directory. 78 * 79 * @return string 80 */ 81 public function getImageCacheDirectory() 82 { 83 return $this->container->getParameter('partkeepr.image_cache_directory'); 84 } 85 86 /** 87 * Ensures that the image cache directory exists. 88 * 89 * @todo Implement checks if the cache directory has actually been created 90 * @todo 0777 might not be the best solution 91 */ 92 public function ensureCacheDirExists() 93 { 94 if (!is_dir($this->getImageCacheDirectory())) { 95 mkdir($this->getImageCacheDirectory(), 0777, true); 96 } 97 } 98 99 /** 100 * Scales the image to fit within the given size. 101 * 102 * @param UploadedFile $image The image to scale 103 * @param int $width The width 104 * @param int $height The height 105 * @param bool $padding If true, pad the output image to the given size (transparent background). 106 * 107 * @return string The path to the scaled file 108 */ 109 public function fitWithin(UploadedFile $image, $width, $height, $padding = false) 110 { 111 $this->ensureCacheDirExists(); 112 113 if ($padding) { 114 $mode = 'fwp'; 115 } else { 116 $mode = 'fw'; 117 } 118 119 $outputFile = $this->getImageCacheFilename($image, $width, $height, $mode); 120 121 if ($this->hasCacheFile($image, $width, $height, $mode) && file_exists($outputFile)) { 122 return $outputFile; 123 } 124 125 $imagine = new Imagine(); 126 127 $localCacheFile = $this->getImageCacheDirectory().$image->getFullFilename(); 128 $storage = $this->get('partkeepr_uploadedfile_service')->getStorage($image); 129 130 file_put_contents($localCacheFile, $storage->read($image->getFullFilename())); 131 132 $imagine->open($localCacheFile) 133 ->thumbnail(new Box($width, $height)) 134 ->save($outputFile); 135 136 $cachedImage = new CachedImage($image, $outputFile); 137 $this->getDoctrine()->getManager()->persist($cachedImage); 138 139 return $outputFile; 140 } 141 142 /** 143 * Returns the path to an image which has been cached in a particular width, height and mode. 144 * 145 * @param UploadedFile $image The image 146 * @param int $width The width 147 * @param int $height The height 148 * @param string $mode The mode 149 * 150 * @return string 151 */ 152 public function getImageCacheFilename(UploadedFile $image, $width, $height, $mode) 153 { 154 $outputFile = $this->getImageCacheDirectory(); 155 $outputFile .= '/'.sha1($image->getFilename()); 156 $outputFile .= $width.'x'.$height.'_'.$mode.'.png'; 157 158 return $outputFile; 159 } 160 161 /** 162 * Checks if the database contains the cache file. 163 * 164 * @param UploadedFile $image 165 * @param $width 166 * @param $height 167 * @param $mode 168 * 169 * @return bool 170 */ 171 protected function hasCacheFile(UploadedFile $image, $width, $height, $mode) 172 { 173 $cacheFilename = $this->getImageCacheFilename($image, $width, $height, $mode); 174 175 /** 176 * @var EntityManager 177 */ 178 $em = $this->getDoctrine()->getManager(); 179 180 $queryBuilder = $em->createQueryBuilder(); 181 $queryBuilder->select('COUNT(c)') 182 ->from('PartKeepr\ImageBundle\Entity\CachedImage', 'c') 183 ->where('c.cacheFile = :file') 184 ->setParameter('file', $cacheFilename); 185 186 if ($queryBuilder->getQuery()->getSingleScalarResult() > 0) { 187 return true; 188 } else { 189 return false; 190 } 191 } 192 }