Фотогалерея своими руками с использованием PERL Часть 2
<<< Часть 1
Рассмотрим и разберем в одтельности каждую процедуру:
процедура просмотра фотогалерей: sub mod_list_gallery {
Загружаем наш шаблон - show.htm:
my ($file);
$file='show.htm'; open(TMP,$file); @temp=<TMP>; close(TMP);
Вырезаем из него сроку списка галерей и строку списка фотографий:
my ($temp_line_1, $temp_line_2, $temp_list_1);
foreach (@temp) {
if (m/<!-- 1 -->/gi) {$_ =~s /<!-- 1 -->//gi; $temp_line_1 .= $_; $_ = '';}
elsif (m/<!-- 2 -->/gi) {$_ =~s /<!-- 2 -->//gi; $temp_line_2 .= $_; $_ = '';}
}
Берем данные о наших фотогалереях из базы данных MySQL:
$sql = "SELECT id, name FROM gallery_table ORDER BY name ASC";
$dbh = "DBI:mysql:$name_base_mysql:$host_base_mysql:$port_base_mysql";
$dbh = DBI -> connect($dbh, $user_base_mysql, $pass_base_mysql);
$sth = $dbh -> prepare($sql); $sth -> execute();
my ($kolvo_category, $i, $tmp, @category, $line_temp);
$i = 0;
$kolvo_category = $sth -> rows;
while ($tmp = $sth -> fetchrow_arrayref()) {$category[0][$i]=$tmp->[0];$category[1][$i]=$tmp->[1];$i++;}
$sth -> finish();
$dbh -> disconnect();
Обрабатываем полученные данные и формируем список фотогалерей:
for ($i=0; $i < $kolvo_category; $i++) {
$line_temp = $temp_line_1;
$line_temp =~s /%name_category%/$category[1][$i]/gi;
$line_temp =~s /%id%/$category[0][$i]/gi;
$temp_list_1 .= $line_temp;
Если у нас выбрана, какая, либо фотогалерея для просмотра фотографий (параметр $edit):
if ($edit eq $category[0][$i]) {
Выбираем список фотографий из базы данных MySQL:
$sql = "SELECT id, name, type_image FROM image_table WHERE gallery = '$category[0][$i]' ORDER BY name ASC";
$dbh = "DBI:mysql:$name_base_mysql:$host_base_mysql:$port_base_mysql";
$dbh = DBI -> connect($dbh, $user_base_mysql, $pass_base_mysql);
$sth = $dbh -> prepare($sql); $sth -> execute();
my (@tv, $kolvo_foto, @photo, $ii)
$tv[0] = 0;
$kolvo_foto = $sth -> rows;
while ($tmp = $sth -> fetchrow_arrayref()) {
$photo[0][$tv[0]] = $tmp -> [0];
$photo[1][$tv[0]] = $tmp -> [1];
$photo[2][$tv[0]] = $tmp -> [2];
$tv[0]++;
}
$sth -> finish();
$dbh -> disconnect();
Формируем список из шаблона:
for ($ii=0;$ii<$kolvo_foto;$ii++) {
$line_temp = $temp_line_2;
$tv[2] = $path_image."i".$photo[0][$ii].".".$photo[2][$ii];
$tv[3] = $path_image.$photo[0][$ii].".".$photo[2][$ii];
$line_temp =~s /%name_page%/$photo[1][$ii]/gi;
$line_temp =~s /%id%/$photo[0][$ii]/gi;
$line_temp =~s /%link_icon%/$tv[2]/gi;
$line_temp =~s /%link_image%/$tv[3]/gi;
$temp_list_1 .= $line_temp;
}
}
Из вышеуказанного кода теперь можно определить, почему мы в таблице фотографий не указывали имени файла, а только расширение - именем файла фотографии у нас будет являться id номер, что предохранит нас от от повторов имен файлов, т.к. поле id ключевое и повторы в нем запрещены, а так же очень просто выставляется соответсвие иконки для определенной картинки, мы просто к имени файла приставляем "i", а остальное остается неизменным.
Теперь остается собрать наш шаблон воедино и выдать на экран:
foreach (@temp) {
$_=~s /<!-- 1_insert -->/$temp_list_1/gi; $_ =~s /%script_url%/$script_url/gi; $_ =~s /%edit%/$edit/gi;
}
print "Content-type: text/html; charset=windows-1251\n\n";
print qq "@temp";
exit;
}
Вот и все с первой процедурой закончили. Берем следующую, самую, на мой взгляд, интересную...
процедура загрузки фотографии: sub mod_image_upload {
Сначала проверяем подтверждение операции над объектом, в данном случае загружаемой картинкой:
if ($doing eq "afform") {
Проверяем имя файла на соответствие разрешенного типа (расширения):
my (@tv, $file)
$tv[0] = $image;
$tv[0] =~s /.*((png)|(gif)|(jpg))$/$1/gi;
if ($tv[0] eq '') {exit;} # Хотя конечно лучше поставить более толковую проверку, но я не буду здесь "раздувать" код
Вставляем данные полученные из формы и полученное расширение файла в MySQL:
$dbh = "DBI:mysql:$name_base_mysql:$host_base_mysql:$port_base_mysql";
$dbh = DBI -> connect($dbh, $user_base_mysql, $pass_base_mysql);
$dbh -> do("INSERT INTO image_table SET name = '$fdb[0]', gallery = '$fdb[1]', type_image = '$tv[0]' ");
$tv[1] = $dbh->{'mysql_insertid'}; # Выберем последний id номер, он будет именем нашего файла
$dbh -> disconnect();
Теперь сохраняем фотографию на сервере:
$file = $path_image.$tv[1].".".$tv[0];
open (IMG, ">$file"); binmode IMG; flock ($file, 2); print IMG while (<$image>); close (IMG); chmod 0644, $file;
Формируем иконку (уменьшенное изображение):
Для начала узнаем какого размера будет иконка:
$sql = "SELECT x_icon, y_icon FROM gallery_table WHERE id='$fdb[1]'";
$dbh = "DBI:mysql:$name_base_mysql:$host_base_mysql:$port_base_mysql";
$dbh = DBI -> connect($dbh, $user_base_mysql, $pass_base_mysql);
$sth = $dbh -> prepare($sql); $sth -> execute();
my ($ix, $iy);
while ($tmp = $sth -> fetchrow_arrayref()) {$ix = $tmp -> [0]; $iy = $tmp -> [1]}
$sth -> finish();
$dbh -> disconnect();
Теперь уменьшаем изображение:
$image = Image::Magick->new;
$image->Read("$file"); # $file - понятно что это только что загруженный файл
Разберемся с тем какой размер иконки должен быть:
my ($ox, $oy, $oc, $ic, $nx, $ny, $geo);
($ox,$oy)=$image->Get('columns','height');
if (($ox > $ix)||($oy > $iy)) {
$oc = $ox/$oy; $ic = $ix/$iy;
if ($oc < $ic) {$ny = $iy; $nx=int(($ox/$oy)*$iy);}
elsif ($oc > $ic) {$nx = $ix; $ny=int(($oy/$ox)*$ix);}
else {$nx = $ix; $ny = $iy;}}
else {$nx=$ox;$ny=$oy;}
Изменяем размер и сохраняем изображение:
$geo = 'geometry';
$image->Resize(geometry=>$geo, width=>$nx, height=>$ny);
$file = $path_image."i".$tv[1].".".$tv[0];
$image -> Write("$file");
"Редиректим" скрипт на список фотогалерей и открываем ту в которою добавили изображение, что бы не было особого желания нажать кнопку "обновить"
$url = $script_url."?edit=".$fdb[1];
print "Location: $url\n\n";
}
Теперь вернемся к тому, что мы пока только хотим произвести действие, здесь, я думаю, особых сложностей не должно возникать:
Выгружаем шаблон:
$file = "upload.htm";
open(TMP, $file);
@temp = <TMP>;
close(TMP);
Вырезаем строку из SELECT для того, что бы сформировать списко галерей для выбора
foreach (@temp) {
if (m/<!-- 1 -->/gi) {$_ =~s /<!-- 1 -->//gi; $temp_line_1 .= $_; $_ = '';}
}
Берем данные о наших фотогалереях из базы данных MySQL*:
$sql = "SELECT id, name FROM gallery_table ORDER BY name ASC";
$dbh = "DBI:mysql:$name_base_mysql:$host_base_mysql:$port_base_mysql";
$dbh = DBI -> connect($dbh, $user_base_mysql, $pass_base_mysql);
$sth = $dbh -> prepare($sql); $sth -> execute();
my ($kolvo_category, $i, $tmp, @category, $line_temp);
$i = 0;
$kolvo_category = $sth -> rows;
while ($tmp = $sth -> fetchrow_arrayref()) {$category[0][$i]=$tmp->[0];$category[1][$i]=$tmp->[1];$i++;}
$sth -> finish();
$dbh -> disconnect();
*Кстати подобная операция, как, впрочем и следующая, будет повторятся практически везде, поэтому её можно вынести в отдельную процедуру, либо выполнять сразу в начале скрипта...
Формируем список фотогалерей:
for ($i = 0; $i < $kolvo_category; $i++) {
$line_temp = $temp_line_1;
if ($cat eq $category[0][$i]) {$line_temp =~s /%selected%/selected/gi;} # вот, кстати, зачем нам нужен был параметр cat - только для этого, что бы было удобно
else {$line_temp =~s /%selected%//gi;}
$line_temp =~s /%id_category%/$category[0][$i]/gi;
$line_temp =~s /%name_category%/$category[1][$i]/gi;
$temp_list_1 .= $line_temp;
}
Собираем шаблон, и выводим на экран:
foreach (@temp) {
$_=~s /<!-- 1_insert -->/$temp_list_1/gi;
$_ =~s /%script_url%/$script_url/gi;
$_ =~s /%mod%/$mod/gi;
$_ =~s /%edit%/$edit/gi;
}
print "Content-type: text/html; charset=windows-1251\n\n";
print qq "@temp";
exit;
}
С этой процедурой все, кратко пробежимся по ней:
- если мы производим операцию над объектом, то есть производим upload изображения
- получаем имя файла (переменная $image - по сути имеет два значения, имя файла и непосредственно файл; и в зависимости от того, как мы используем эту переменную, то получаем соответстующее её значение);
- выбираем расширение файла, в нашем случае ((png)|(gif)|(jpg)) - разрешенные расширения файлов которые мы можем загружать;
- вставляем данные об изображении в таблицу базы данных MySQL;
- производми upload файла;
- сразу же считываем только что загруженный файл в новый проект Image::Magick;
- определяем размеры изображения, до которого мы должны его ужать;
- сжимаем изображение и записываем в новый файл;
- редиректим скрипт;
- если же мы только хотим загрузить файл, то просто выводим обработанную форму и все.
С этой процедурой все, кратко пробежимся по ней:
процедура изменения и процедура удаления фотографии: sub mod_image_edit { sub mod_image_delete {
Данные процедуры отличаются лишь тем, что при переданном параметре doing - первая производит UPDATE таблицы MySQL, а вторая DELETE FROM. А изменение изображения сводится только к изменению названия, описания и галереи изображения, правда есть некоторые нюансы:
Сначала проверяем подтверждение операции над объектом, в данном случае - картинкой:
if ($doing eq "afform") {
Обновляем таблицу если мы редактируем изображение
$dbh = "DBI:mysql:$name_base_mysql:$host_base_mysql:$port_base_mysql";
$dbh = DBI -> connect($dbh, $user_base_mysql, $pass_base_mysql);
$dbh -> do("UPDATE image_table SET name = '$fdb[0]', gallery = '$fdb[1]' WHERE id = '$id'");
$dbh -> disconnect();
Изменяем иконку, изображения, так как в разных фотогалереях могут быть разные размеры иконок (данную операцию я бы рекомендовал производить после проверки изменеия галереи фотографии, что бы лишний раз не переписывать иконку)
Эта операция полностью идентична опрерации изменения иконки зображения при upload, единственное прежде нужно взять данные об изображении (id номер и расширение (type-image)). После изменения иконки производим редирект в список фотогалерей
Удаляем запись из таблицы если мы удаляем изображение:
Сначала считываем данные о этом изображении, для того, что бы мы могли удалить соответственные файлы:
$sql = "SELECT id, type_image FROM image_table WHERE id = '$id'";
$dbh = "DBI:mysql:$name_base_mysql:$host_base_mysql:$port_base_mysql";
$dbh = DBI -> connect($dbh, $user_base_mysql, $pass_base_mysql);
$sth = $dbh -> prepare($sql); $sth -> execute();
my (@photo)
while ($tmp = $sth -> fetchrow_arrayref()) {
$photo[0][0] = $tmp -> [0];
$photo[1][0] = $tmp -> [1];
}
$sth -> finish();
Удаляем запись:
$dbh -> do("DELETE FROM image_table WHERE id = '$id'");
$dbh -> disconnect();
$file = $path_image."i".$photo[0][0].".".$photo[1][0]; unlink ($file);
$file = $path_image.$photo[0][0].".".$photo[1][0]; unlink ($file);
Опять же редирект...
}
Теперь вернемся к тому, что мы пока только хотим произвести действие, здесь, я думаю, тоже особых сложностей не должно возникать, алгоритм такой же, что и при upload, единственное мы сначало должны получить данные об изображении из MySQL и вставить их в форму:
$file="edit.htm";
open(TMP,$file);
@temp=<TMP>;
close(TMP);
foreach (@temp) {
if (m/<!-- 1 -->/gi) {$_ =~s /<!-- 1 -->//gi; $temp_line_1 .= $_; $_ = '';}
}
my ($kolvo_category, $i, $tmp, @category, $line_temp, @photo);
$sql = "SELECT id, name, gallery, image FROM image_table WHERE id = '$id'";
$dbh = "DBI:mysql:$name_base_mysql:$host_base_mysql:$port_base_mysql";
$dbh = DBI -> connect($dbh, $user_base_mysql, $pass_base_mysql);
$sth = $dbh -> prepare($sql); $sth -> execute();
while ($tmp = $sth -> fetchrow_arrayref()) {
$photo[0][0] = $tmp -> [0];
$photo[1][0] = $tmp -> [1];
$photo[2][0] = $tmp -> [2];
$photo[3][0] = $tmp -> [3];}
$sth -> finish();
$sql = "SELECT id, name FROM gallery_table ORDER BY name ASC";
$dbh = "DBI:mysql:$name_base_mysql:$host_base_mysql:$port_base_mysql";
$dbh = DBI -> connect($dbh, $user_base_mysql, $pass_base_mysql);
$sth = $dbh -> prepare($sql); $sth -> execute();
$i = 0;
$kolvo_category = $sth -> rows;
while ($tmp = $sth -> fetchrow_arrayref()) {$category[0][$i]=$tmp->[0];$category[1][$i]=$tmp->[1];$i++;}
$sth -> finish();
$dbh -> disconnect();
for ($i = 0; $i < $kolvo_category; $i++) {
$line_temp = $temp_line_1;
if ($photo[2][0] eq $category[0][$i]) {$line_temp =~s /%selected%/selected/gi;}
else {$line_temp =~s /%selected%//gi;}
$line_temp =~s /%id_category%/$category[0][$i]/gi;
$line_temp =~s /%name_category%/$category[1][$i]/gi;
$temp_list_1 .= $line_temp;
}
$tv[2] = $image_url."i".$photo[0][0].".".$photo[3][0];
$tv[3] = $image_url.$photo[0][0].".".$photo[3][0];
foreach (@temp) {
$_=~s /<!-- 1_insert -->/$temp_list_1/gi;
$_=~s /%name%/$photo[1][0]/gi;
$_=~s /%link_image%/$tv[3]/gi;
$_=~s /%link_icon%/$tv[2]/gi;
$_=~s /%doing%/Изменение || Удаление/gi; # В зависимости от действия
$_ =~s /%script_url%/$script_url/gi;
$_ =~s /%mod%/$mod/gi;
$_ =~s /%edit%/$edit/gi;
$_ =~s /%id%/$photo[0][0]/gi;
}
print "Content-type: text/html; charset=windows-1251\n\n";
print qq "@temp";
exit;
Вот в общем-то все процедуры работы с изображениями рассмотрели, теперь процедуры работы с фотогалереями:
Сразу хочу сказать, что различие между процедурами создания, изменения и удаления минимальны:
- в процессе создания мы ничего не проверяем толко данные переданные из формы;
- в процессе редактирования мы проверяем изменились ли параметры x_icon и y_icon, и если да, то переписываем все иконки изображений в соответсвии с новыми параметрами;
- в процессе удаления, мы проверяем есть ли в данной фотогалереи фотографии и если есть отказываем в удалении, чтобы не оставалось "безхозных" фотографий
;
Структура же процедур точно такая же как и у процедур работы с изображениями;
С админ-интерфейсом все, теперь перейдем к внешней части, собственно что теперь должны увидеть пользователи...
ОПИСАНИЕ СКРИПТА ПОКАЗА ФОТОГАЛЕРЕИ:
Сам шаблон фотогалереи у нас находит ся в таблице базы данных MySQL, а так как, у нас может быть несколько фотогалерей, то и шаблонов будет столько же, и при запуске нашего скрипта нужно будет указывать какую фотогалерею мы используем. То есть скрипт сам по себе по ссылке //www.mysite.ru/cgi-bin/gallery.cgi - работать не будет, точнее будет, но покажет при этом пустую страницу, для этого ссылку нужно будет делать //www.mysite.ru/cgi-bin/gallery.cgi?gal=1, где gal - id номер фотогалереи. Если же мы будем использовать только одну галерею, то данный параметр мы можем вписать прямо в скрипт, и тогда не будет никаких "заморочек".
Я предполагаю для себя такую структуру шаблона:
Что в HTML коде будет выглядеть как:
<html>
<head>
<meta http2-equiv="Content-Type" content="text/html; charset=windows-1251">
<title>Моя фотогалерея</title>
</head>
<body>
<table width="98%" border="0" align="center" cellpadding="4" cellspacing="1">
<tr>
<td>
%name_category%
</td>
</tr>
<tr>
<td>
%title_category%
</td>
</tr>
<!-- 1_insert -->
<!-- 1 --> <tr>
<!-- 1 --> <td>
<!-- 1 --> Страницы <
<!-- 2 --> <a href="%link_page%">%n_page%</a>
<!-- 3 --> <b>%n_page%</b>
<!-- 1 --><!-- 2_insert -->
<!-- 1 --> >
<!-- 1 --> </td>
<!-- 1 --> </tr>
<!-- 4_insert -->
<!-- 4 --> <tr>
<!-- 4 --><!-- 5_insert -->
<!-- 5 --> <td>
<!-- 5 --> <a href="%link_image%" target="_blank"><img src="%link_icon%" alt="%name_image%"></a><br>
<!-- 5 --> %name_image%
<!-- 5 --> </td>
<!-- 4 --> </tr>
<!-- 1_insert -->
</table>
</body>
</html>
В блоках «заголовок» и «окончание» используются следующие переменные:
- %name_category% - Название категории;
- %title_category% - Описание категории;
В блоке «фотография» используются следующие переменные:
- %link_image% - Ссылка на фотографию;
- %link_icon% - Ссылка на уменьшенное изображение;
- %name_image% - Название фотографии;
В блоке «список страниц» используются следующие переменные:
- %link_page% - Ссылка на предыдущую/следующую страницу;
- %n_page% - Номер страницы;
Теперь перейдем скрипту, естественно он должен быть компактен и работать в один проход, то есть процедуры мы не используем:
Начало стандартное:
#!/usr/bin/perl
use CGI;
use DBI;
use DBD::mysql;
use strict;
my ($query, $gallery, $page, $dbh, $sth, $sql, $tmp, @cat, $mi, $i, $max_img, @img, @temp, $tl1, $tl2, $tl3, $tl4, $tl5, $np, $ii, $tl, $link, $list2, $n_row, $n_col, $list4, $list5, $iii, $url_img, $l_img, $l_ico);
Устанавливаем начальные переменные:
$url_img = "//www.mysite.ru/gallery/";
Получаем переменные окружения:
$query = new CGI;
$gallery = $query -> param('gal');
$page = $query -> param('p');
$gallery = 0 if $gallery eq ''; # MySQL уходит в "штопор" если указывается пустое значение в условии запроса
$page = 1 if $page eq '';
Выбираем данные нашей фотогалереи:
$dbh = "DBI:mysql:mybase:localhost:3306";
$dbh = DBI->connect($dbh,'user','qwerty');
$sql = "SELECT name, titling, template, row_list, column_list FROM gallery_table WHERE id='$gallery'";
$sth = $dbh -> prepare($sql); $sth -> execute();
while ($tmp = $sth -> fetchrow_arrayref()) {
$cat[0] = $tmp->[0];
$cat[1] = $tmp->[1];
$cat[2] = $tmp->[2];
$cat[3] = $tmp->[3];
$cat[4] = $tmp->[4]
}
$sth -> finish();
Обрабатываем полученные данные:
$mi = $cat[3]*$cat[4]; # количество фотографий на странице
$i = ($page-1)*$mi; # "стартовая точка" для LIMIT
@temp = split(/\n/,$cat[2]); # переводим шаблон в массив, так как его обработка ведется построчно
Выбираем количество фотографий фотогалереи:
$sql = "SELECT id FROM image_table WHERE category='$gallery'";
$sth = $dbh -> prepare($sql); $sth -> execute();
$max_img = $sth->rows;$sth->finish();
Выбираем список фотографий фотогалереи:
$sql = "SELECT id,name,image FROM image_table WHERE category='$gallery' LIMIT $i,$mi";
$sth = $dbh -> prepare($sql); $sth -> execute(); $i = 0;
while ($tmp=$sth->fetchrow_arrayref()) {
$img[0][$i] = $tmp->[0];
$img[1][$i] = $tmp->[1];
$img[2][$i] = $tmp->[2];
$i++}
$sth -> finish();$dbh -> disconnect();
Вырезаем из шаблона блоки:
foreach (@temp) {
if (/<!-- 1 -->/) {$_=~s/<!-- 1 -->//g; $tl1 .= $_; $_=''}
if (/<!-- 2 -->/) {$_=~s/<!-- 2 -->//g; $tl2 .= $_; $_=''}
if (/<!-- 3 -->/) {$_=~s/<!-- 3 -->//g; $tl3 .= $_; $_=''}
if (/<!-- 4 -->/) {$_=~s/<!-- 4 -->//g; $tl4 .= $_; $_=''}
if (/<!-- 5 -->/) {$_=~s/<!-- 5 -->//g; $tl5 .= $_; $_=''}}
Формируем блок количества страниц:
if ($mi eq 0) {$mi = 1} # проверка что-бы не было деления на 0
$np = int(($max_img - 1)/$mi)+1; # количество страниц
if ($np eq 1) {$tl1 = ''} # если всего одна страница то данный блок не выводим, точнее обнуляем
else {
for ($ii = 1; $ii < ($np + 1); $ii++) {
if ($ii eq ($page)) {$tl = $tl3} else {$tl = $tl2} # текущая/нетекущая страница
$link = "?gal=".$gallery."&p=".$ii;
$tl =~s/%n_page%/$ii/gi;
$tl =~s/%link_page%/$link/gi;
$list2 .= $tl;
}
$tl1 =~s/<!-- 2_insert -->/$list2/gi;
}
Получаем количество строк:
$n_row = int(($i-1)/$cat[4])+1;
Формируем строки:
for ($ii = 0; $ii < $n_row; $ii++) {
if ($ii eq ($n_row-1)) {$n_col = $i - $ii*$cat[4]} else {$n_col = $cat[4]} # получаем количество ячеек в строке
$list5 = '';
Формируем ячейки:
for ($iii = 0; $iii < $n_col; $iii++) {
$id = $ii*$cat[4] + $iii;
$l_img = $url_img.$img[0][$id].".".$img[2][$id];
$l_ico = $url_img."i".$img[0][$id].".".$img[2][$id];
$tl = $tl5;
$tl =~s/%link_image%/$l_img/gi;
$tl =~s/%link_icon%/$l_ico/gi;
$tl =~s/%name_image%/$img[1][$id]/gi;
$list5 .= $tl;
}
Вставляем сформированные ячейки в строку:
$tl=$tl4;
$tl=~s/<!-- 5_insert -->/$list5/gi;
$list4 .=$tl;
}
Последняя обработка шаблона: вставляем сформированные строки и список страниц:
foreach (@temp) {
$_ =~s/<!-- 1_insert -->/$tl1/gi;
$_ =~s/<!-- 4_insert -->/$list4/gi;
$_ =~s/%name_category%/$cat[0]/gi;
$_ =~s/%title_category%/$cat[1]/gi;}
Выводим на экран:
print "Content-type: text/html; charset=windows-1251\n\n";print qq "@temp";exit;
и все... нет, конечно, еще нужно поработать с шаблономи и привести их в соответствующий вид, но это уже дело техники...
Томулевич Сергей (Phoinix) www.asit.ru
|