Image helper – lazy generování náhledů
- Ondřej Mirtes
- Člen | 1536
Ahoj,
vytvořil jsem první nástřel, jak bych si představoval „lazy“
generování náhledů na webu. Vešlo se mi to do jedné metody, tak
posuďte :)
Chtěl jsem to udělat trochu univerzálnější, ale kvůli tomu, jak Nette parsuje parametry helperů (obalí je uvozovkami, takže je nelze převzít jako instanci objektu) se mi to nepodařilo. Ty části jsou tedy zatím zakomentované. Odkomentoval jsem to, ty Html objekty můžete tvořit v Presenteru.
Taky jsem zakomentoval dvě proměnné s cestami, které jsem si vytvořil,
ale pak zjistil, že je nepotřebuji :)
A taky doufám, že detekce všech cest (je jich hodně) je spolehlivá.
Při neexistenci souboru s obrázkem vracím string, že neexistuje (nechci shodit celou aplikaci kvůli chybějícímu obrázku, proto nevyhazuji exception), ale pokud neexistuje složka, do které se má zapsat náhled, tak už Exception klidně vyhodím, to si měl admin zařídit :))
Dále jsem váhal, jestli poslední parametr nemá být boolean $needLink,
ale to by omezilo univerzálnost a dám tam tedy Html $wrapper
,
přičemž pak budu detekovat, jestli se jedná o odkaz a pokud ano a zároveň
neobsahuje atribut href
, doplním ho cestou k originálnímu
(velkému obrázku).
Použití
{* Cesta k obrázku musí být absolutní, parametry metody jsou: width, height, alt *}
{=WWW_DIR . $baseUri . 'images/image.jpg' |image:100,50,'Trance cézet'}
Zdrojový kód
public static function image($absolutePath, $width, $height, $alt='', Html $img=NULL, Html $wrapper=NULL) {
if (!file_exists($absolutePath)) {
return 'Image not found.';
}
$absoluteDirPath = dirname($absolutePath);
$filename = basename($absolutePath);
$explodedFilename = explode('.', $filename);
$extension = $explodedFilename[count($explodedFilename)-1];
$basename = substr($filename, 0, -strlen($extension)-1);
$thumbnailFilename = $basename . '-thumb-' . $width . '-' . $height . '-' . filemtime($absolutePath). '.' . $extension;
$absoluteThumbnailDirPath = $absoluteDirPath . '/thumbnails';
$absoluteThumbnailPath = $absoluteThumbnailDirPath . '/' . $thumbnailFilename;
$webThumbnailPath = substr($absoluteThumbnailPath, strlen(WWW_DIR));
if (!is_dir($absoluteThumbnailDirPath) || !is_writable($absoluteThumbnailDirPath)) {
throw new InvalidStateException('Thumbnail path ' . $absoluteThumbnailDirPath . ' does not exists or is not writable.');
}
if (file_exists($absoluteThumbnailPath)) {
$image = Image::fromFile($absoluteThumbnailPath);
} else {
$image = Image::fromFile($absolutePath);
$image->resize($width, $height);
$image->save($absoluteThumbnailPath);
}
if ($img === NULL) {
$img = Html::el('img');
}
$img->src = $webThumbnailPath;
$img->width = $image->width;
$img->height = $image->height;
$img->alt = $alt;
if ($wrapper !== NULL) {
return $wrapper->add($img);
}
return $img;
}
Editoval Ondřej Mirtes (25. 1. 2010 22:42)
- Ondřej Mirtes
- Člen | 1536
Je to spíš kus kódu než komponenta :) Zkusím to nějak obalit (extendovat Image?) a vydat :)
A taky jsem přišel během používání na bug – mám ho v jednom projektu opravenej, ale už nevím, co to bylo :)
EDIT: Bug opraven i v původním příspěvku (šlo o to, že pokud jste aktualizovali originální obrázek, tak se náhled neupdatoval, proto jsem do cesty náhledu přidal i filemtime originálu) + kontroluju zapisovatelnost složky pro náhledy :)
Asi to vydám tak, jak to je, protože s obalením nějaké třídy (a ubráním static) by bylo zase složitější to zaregistrovat jako helper, což je tady asi ta killer feature :)
Editoval Ondřej Mirtes (24. 1. 2010 23:58)
- Honza Kuchař
- Člen | 1662
Já bych nic nextendoval. A zařadil bych to sem: https://componette.org/search/?q=
- Honza Kuchař
- Člen | 1662
Používám na jednom webu (bez Nette) něco podobného a vím jak je to užitečné. :) Docela mě překvapilo, že to tu jen tak leží bez povšimnutí.
- Michalek
- Člen | 211
Pokud mám velký web s hodně fotkama (počítám 1.000.000+) a všude bych to dělal takhle, asi by to bylo dost náročné, ne? (neberu v úvahu cachování výsledného html)
V současnosti jsem se inspiroval řešením zmíněným v jednom
příspěvku tady a pokud mi obrázek na adrese
/storage/obrazek/uzel/2002/05/48283897ee46c/4b32d35f9c437/90_90_crop.jpg
neexistuje, přesměruju přes .htaccess
požadavek na „lazy
generátor“, ten požadovaný obrázek vygeneruje dle parametrů z
90_90_crop.jpg
, uloží na místo a přesměruje se zase tam.
Mám tam dva nevyřešené problémy
- když změním 90 na 91, zůstanou mi tam zbytečně vygenerované obrázky (což tenhle lazy generátor taky neřeší)
- když někdo napíše skript na načítání obrázků od 1_1.jpg do 1000_1000.jpg asi mi zavaří server :)
Editoval Michalek (26. 1. 2010 14:12)
- rokerkony
- Člen | 122
tak tenhle generator take nezmensuje obrazek vzdy ale jen tehdy pokud jeste
neexistuje. Pokud existuje tak se jen precte. cili v podstate asi stejne reseni
jak popisujes.
S temi problemy asi moc reseni neexistuje… jedine co me napada pro to prvni
je obcas to smazat cronem a nechat stranku nacist znova a timm vygenerovat
obrazky, ale to je zase silene narocne… :-/
- uestla
- Backer | 799
Rozhodl jsem se také přidat trochu do vínka ;)
// EDIT: aktualizoval jsem kód, je tam pár drobných změn
Mám třídu LayoutHelpers
a v ní podobnou metodu na
vytváření miniatury – s tím rozdílem, že zbytečně nevytváří pro
každý zmenšovaný obrázek instatci Image
a nevrací instance
Html
, nýbrž cestu – tak, aby byl tag <img /> kompletně
v režii šablonáře.
Kód metody:
abstract class LayoutHelpers extends Object
{
/*************************** vytvareni miniatur ***************************/
/** @var string relativni URI k adresari s miniaturami (zacina se v document_rootu) */
public static $thumbDirUri = NULL;
/**
* Vytvoreni miniatury obrazku a vraceni jeho URI
*
* @param string relativni URI originalu (zacina se v document_rootu)
* @param NULL|int sirka miniatury
* @param NULL|int vyska miniatury
* @return string absolutni URI miniatury
*/
public static function thumb($origName, $width, $height = NULL)
{
$thumbDirPath = WWW_DIR . '/' . trim(self::$thumbDirUri, '/\\');
$origPath = WWW_DIR . '/' . $origName;
if (($width === NULL && $height === NULL) || !is_file($origPath) || !is_dir($thumbDirPath) || !is_writable($thumbDirPath))
return $origName;
$thumbName = self::getThumbName($origName, $width, $height, filemtime($origPath));
$thumbUri = trim(self::$thumbDirUri, '/\\') . '/' . $thumbName;
$thumbPath = $thumbDirPath . '/' . $thumbName;
// miniatura jiz existuje
if (is_file($thumbPath)) {
return $thumbUri;
}
try {
$image = Image::fromFile($origPath);
// zachovani pruhlednosti u PNG
$image->alphaBlending(FALSE);
$image->saveAlpha(TRUE);
$origWidth = $image->getWidth();
$origHeight = $image->getHeight();
$image->resize($width, $height,
$width !== NULL && $height !== NULL ? Image::STRETCH : Image::FIT)
->sharpen();
$newWidth = $image->getWidth();
$newHeight = $image->getHeight();
// doslo ke zmenseni -> ulozime miniaturu
if ($newWidth !== $origWidth || $newHeight !== $origHeight) {
$image->save($thumbPath);
if (is_file($thumbPath))
return $thumbUri;
else
return $origName;
} else {
return $origName;
}
} catch (Exception $e) {
return $origName;
}
}
/**
* Vytvori jmeno generovane miniatury
*
* @param string relativni cesta (document_root/$relPath)
* @param int sirka
* @param int vyska
* @param int timestamp zmeny originalu
* @return string
*/
private static function getThumbName($relPath, $width, $height, $mtime)
{
$sep = '.';
$tmp = explode($sep, $relPath);
$ext = array_pop($tmp);
// cesta k obrazku (ale bez pripony)
$relPath = implode($sep, $tmp);
// pripojime rozmery a mtime
$relPath .= $width . 'x' . $height . '-' . $mtime;
// zahashujeme a vratime priponu
$relPath = md5($relPath) . $sep . $ext;
return $relPath;
}
}
Když pak helper registruji v Presenteru, zadám také cestu k adresáři pro miniatury:
abstract class BasePresenter extends Presenter
{
protected function beforeRender()
{
parent::beforeRender();
LayoutHelpers::$thumbDirUri = 'images/thumbs';
$this->template->registerHelper('thumb', 'LayoutHelpers::thumb');
}
}
No a jsme u cíle – samotné použití v šabloně:
<img src="{=$basePath}/{='images/obrazek.jpg'|thumb:150}">
// EDIT: tento helper jde snadno kombinovat s Texy! (vizte vlákno na Texy! fóru)
Editoval uestla (23. 10. 2010 5:38)
- Roman Ožana
- Člen | 52
Včera jsem se trošku inspiroval a napsal svůj helper https://gist.github.com/547309