Fotoalbum v NETTE (flickr style) – práce s fotkami

Myšlena práce s fotkami na straně serveru…

Co bylo třeba vyřešit:

  1. zmenšení fotek
  2. záloha velkých originálů
  3. vytvoření náhledů
  4. vytvoření úvodní fotky pro danou složku
  5. (vyplynulo až později) „kešování“ dat, pro dostatečně rychlé procházení fotoalbem

Všechno se děje vlastně sériově. Vždy se ověří, zda danou operaci je nutné dělat a pokud ne, tak se vynechá. Zároveň se dané části kešují, jelikož z praxe vyplynulo, že i když není třeba nic dělat, tak samotné ověřování trvá řády jednotek až desítek sekund. A na to nikdo čekat nechce.

Poznámka: Vím, že kód níže není optimální a zasloužil by jistou refaktorizaci, ale to bych tento článek a především fotoalbum nenapsal ani za rok (Ano píšu ho ve svém volném čase a pro dobro věci. Ne pro peníze.). A taky se omlouvám za ne úplně 100% prezentaci kódu, ale neznám lepší plugin do WordPressu, který by to zvládal dokonale.

# VYTÁHNEMKE SI SLOŽKY A K NIM ÚVODNÍ FOTKU (samozřejmě jen pokud již nejsou v cache)
$value = $cache->load("folders-{$path}");
if ($value === NULL) {
	$i=0;
	foreach (Finder::findDirectories('*')->exclude('[0-9][0-9][0-9]', '_big')->in($path)->orderByName('DESC') as $key => $file) {
		$this->template->dirs[$i]['name'] = $this->I->getNameFromPath(substr($key, strlen(ALBUM_PATH)).'/');
		// vytahneme si small_home
		foreach (Finder::findFiles('_small_home.png')->in($key) as $key_home => $file_home) {
			$this->template->dirs[$i]['small_home'] = ALBUM_DIR . substr($file_home->getPath(), strlen(ALBUM_PATH)) . '/_small_home.png';
		}
		$this->template->dirs[$i++]['pos'] = substr($key, strlen(ALBUM_PATH)+5);
	}
	$cache->save("folders-{$path}", $this->template->dirs);
} else {
	$this->template->dirs = $value;
}
 
# VYTÁHNEMKE SI FOTKY (samozřejmě jen pokud již nejsou v cache)
$value = $cache->load("files-f-{$path}");
if ($value === NULL) {
	# POKUD EXISTUJÍ TAKOVÉ, KTERÉ MAJÍ VĚTŠÍ STRANU VĚTŠÍ NEŽ 1200PX, TAK JE:
	#	- ZMENŠÍME
	# 	- VELKÉ ORIGINÁLY ZAZÁLOHUJEME
	# 	- PŘENESEME EXIF INFORMACE			
	foreach (Finder::findFiles('*.jpg', '*.jpeg')->exclude('*_small*')->dimensions('>1200', '>1200')->in($path) as $key => $file) {
		$image = @Image::fromFile($key); // ini_set(‘gd.jpeg_ignore_warning’, 1);
 
		$image->resize(1200, 1200, Image::SHRINK_ONLY | Image::FIT);
 
		$ex = exif_read_data($key, 'EXIF');
		if(!empty($ex['Orientation'])) {
			switch($ex['Orientation']) {
				case 8:
					$image->rotate(90, 0);
					break;
				case 3:
					$image->rotate(180, 0);
					break;
				case 6:
					$image->rotate(-90, 0);
					break;
			}
		}
 
		$image->sharpen();
 
		$s = $key;
		$d = $file->getPath() . '/_big/' . $file->getFilename();
		$fd = new SplFileInfo($file->getPath() . '/_big/');
		if (!$fd->isDir()) mkdir($file->getPath() . '/_big/');
		rename($s, $d);
		$image->save($key);
 
		// přeuložíme EXIF informace - použita externí knihovna PelJpeg
		$input_jpeg = new PelJpeg($d);
		$exif = $input_jpeg->getExif();
		if ($exif != null) {
			$output_jpeg = new PelJpeg($key);
			$output_jpeg->setExif($exif);
			$output_jpeg->saveFile($key);
		}
	}
 
	# DÁLE VYTVOŘÍME MALÉ NÁHLEDY
	foreach (Finder::findFiles('*.jpg', '*.jpeg')->exclude('*_small*')->exclude($denyFiles)
		->filter(function($file) {
			$file_extension = pathinfo($file->getFilename(), PATHINFO_EXTENSION);
			$info = new SplFileInfo($file->getPath() . '/' . $file->getBasename('.' . $file_extension) . '_small_500' . '.jpg');
			return !$info->isFile();
		})->in($path) as $key => $file) {
 
		// vytvoření náhledu
		$image = @Image::fromFile($key);
		$image->resize(500, 500, Image::SHRINK_ONLY | Image::FIT);
 
		$image->sharpen();
		$file_extension = pathinfo($file->getFilename(), PATHINFO_EXTENSION);
		$image->save($file->getPath() . '/' . $file->getBasename('.' . $file_extension) . '_small_500' . '.jpg');
	}
 
	# DÁLE VYTVOŘÍME ÚVODNÍ NÁHLED, KTERÝ SE BUDE ZOBRAZOVAT MÍSTO OBRÁZKU SLOŽKY
	$small_home = Image::fromBlank(360, 360, Image::rgb(255, 255, 255, 127));
	$i=0;
	foreach (Finder::findFiles('*.jpg', '*.jpeg')->exclude('*_small*')->exclude($denyFiles)
		->filter(function($file) {
			$info = new SplFileInfo($file->getPath() . '/_small_home.png');
			return !$info->isFile();
		})->in($path) as $key => $file) {
		$image = @Image::fromFile($key);
		$image->saveAlpha(true);
		$image->resize(250, 250, Image::SHRINK_ONLY | Image::FIT);
		$blank = Image::fromBlank($image->getWidth()+20, $image->getHeight()+20, Image::rgb(255, 255, 255));
		$blank->alphaBlending(true);
		$blank->saveAlpha(true);
 
		$blank->place($image, 10, 10);
 
		$blank->rotate(mt_rand(-60, 60), Image::rgb(255, 255, 255, 127));
		$blank->sharpen();
 
		$small_home->place($blank, 20+mt_rand(-15, 15), 20+mt_rand(-15, 15));
 
		if ($i++ == 5) {
			$small_home->resize(200, 200, Image::SHRINK_ONLY | Image::FIT);
			$small_home->save($file->getPath() . '/_small_home.png', NULL, Image::PNG);
 
			$upPath = substr($file->getPath(), 0, strrpos($file->getPath(), '/')+1);
			$cache->remove("folders-{$upPath}"); // pokud tvoříme náhled, tak smažeme cache předka složky, aby se mi při dalším načtení tato cache obnovila
			break;
		}
	}
 
	# A KDYŽ MÁME VŠECHNO PŘIPRAVENO, TAK SI JEŠTĚ JEDNOU NAČTU KOMPLETNÍ INFO, KTERÉ SI PAK ULOŽÍM DO CACHE
	$i=0;
	foreach (Finder::findFiles('*.jpg', '*.jpeg')->exclude('*_small*')->exclude($denyFiles)->in($path)->orderByEXIFTime() as $key => $file) {
		$this->template->files[$i]['key'] = ALBUM_DIR . substr($key, strlen(ALBUM_PATH));
		$exif_ifd0 = read_exif_data($file, 'IFD0', 0);
		$this->template->files[$i]['file'] = date("d.m.y H:i", strtotime($exif_ifd0['DateTime']));
 
		$file_extension = pathinfo($file->getFilename(), PATHINFO_EXTENSION);
		$tf = @Image::fromFile($file->getPath() . '/' . $file->getBasename('.' . $file_extension) . '_small_500' . '.jpg');
		$this->template->files[$i]['w'] = $tf->getWidth(); 
		$this->template->files[$i]['h'] = $tf->getHeight(); 
		$this->template->files[$i++]['pos'] = ALBUM_DIR . substr($file->getPath(), strlen(ALBUM_PATH)) . '/' . $file->getBasename('.' . $file_extension) . '_small_500' . '.jpg';
	}
	$cache->save("files-f-{$path}", $this->template->files);
} else {
	$this->template->files = $value;
}

Jednotlivé části dle mě nemá smysl detailněji popisovat. Je třeba se tím prokousat a pochopit. Pokud by byly dotazy nechť poslouží komentáře.

Jedna konstrukce by šla napsat jednodušeji, jen DEBIAN v současnosti neobsahuje správnou verzi PHP a v té co tam je obsahuje známou chybu…

// Takže místo...
$tf = @Image::fromFile($file->getPath() . '/' . $file->getBasename('.' . $file->getExtension()) . '_small_500' . '.jpg');
// musím používat toto...
$file_extension = pathinfo($file->getFilename(), PATHINFO_EXTENSION);
$tf = @Image::fromFile($file->getPath() . '/' . $file->getBasename('.' . $file_extension) . '_small_500' . '.jpg');

Napsat komentář

Vaše emailová adresa nebude zveřejněna.