Практические примеры программирования cgi-скриптов на Perl: работа с файлами и каталогами.
Работа с файлами и каталогами.
Чтение и запись файлов и каталогов является едва ли не главным достоинством Perl.
Практически любой Perl-скрипт использует либо запись в файлы,либо поиск определенных строк в файле,либо запись или загрузку файлов в каталоги.
Кратко напомню основные положения:
Файл можно открывать для чтения,записи,добавления либо поиска определенных строк.
Открыть файл для чтения: open (FILE,"$file");
Открыть файл для записи: open (FILE,">$file");
Открыть файл для добавления: open (FILE,">>$file");
Открыть файл для чтения и добавления: open (FILE,"+<$file");
Где FILE-это дескриптор или описатель файла,т.е. имя,под которым он фигурирует в программе.Может быть любым набом символов,рекомендуется набирать прописными буквами во избежание неоднозначностей.
$file-переменная для файла,содержит обычно имя файла и путь к нему.Предпочтительно объявить эту переменную в начале скрипта.Это удобно,если файл находится где-то глубоко в подкаталогах,к примеру,неудобно все время набирать /usr/local/htdocs/main/pages/file.html.
Думаю,с этим особых вопросов не возникнет.Еще нужно отметить,что содержимое файла можно читать построчно или в массив:
#!/usr/local/bin/perl
open (STAT,"$file");#Прочесть одну строку из файла.
$count=;
close (STAT);
-------------------------------------------------
#!/usr/local/bin/perl
open (STAT,"$file");#Прочесть файл в массив.
@count=;
close (STAT);
Что касается каталога,то его тоже можно открывать для чтения командой readdir.Для того,чтобы понять как все это происходит,рассмотрим практические примеры.
Пример 1.
Рассмотрим сценарий регистрации пользователя на веб-сервере.Имя пользователя и его пароль записываются в текстовый файл и используются для его последующей аутентификации.
#!/usr/local/bin/perl
#Объявляем глобальные переменные.
$request=$ENV{'REQUEST_METHOD'};
$content=$ENV{'CONTENT_LENGTH'};
$basedir="//www.mydomain.com/~";
$userdir="f:/home";
#Подпрограммы для декодирования данных из формы.
sub urldecode {
local($val)=@_;
$val=~ s/\+/ /g;
$val=~ s/%[0-9a-hA-H] {2}/pack('C',hex($1))/ge;
return $val;
}
sub strhtml {
local($val)=@_;
$val=~s//>/g;
$val=~s/(\/\/\+S)/<A href="$1">$1<\/A>/g;
return $val;
}
######################################################################
if ($request eq 'GET') {
$query=$ENV{'QUERY_STRING'};
}
else {
sysread(STDIN,$query,$content);
}
#Генерируем форму,если никакие данные не введены.
print "Content-type:text/html\n\n";
print <<HTML_gen;
<HTML><BODY bgcolor="e6e8fa">
HTML_gen
if ($query eq '') {
print <<HTML;
<h2 align=center><font color="ff0000">Registration.</font></h2>
<p><font face="serif" size=2> Please,fill in the form below.
<p>After registration you will receive your personal directory and unique
URL.Fill all fields carefully.
Form fields marked as <font color="ff0000">*</font>are required.</font>
<p><FORM ACTION="../cgi-bin/addlogin.cgi" METHOD="POST" name="reg">
<center><TABLE BGCOLOR="bfbfbf">
<TR><td><font color="ff0000">*</font>
<TD><b>Login:</b><TD><INPUT TYPE="text" NAME="login" SIZE="20">
<TR><td><font color="ff0000">*</font>
<TD><b>Password:</b>
<TD><INPUT TYPE="password" NAME="pass" SIZE="20">
<TR><td><font color="ff0000">*</font><TD><b>E-mail:</b>
<TD><INPUT TYPE="text" NAME="email" SIZE="20">
<TR><TD colspan=3><p><center>
<INPUT TYPE="submit" VALUE="Submit"></center>
</TABLE></center>
</FORM>
HTML
#Декодируем поля формы
else {
foreach (@fields=split(/&/,$query)) {
if (/^login=(.*)/) { $login=&urldecode ($1); }
if (/^pass=(.*)/) { $password=&urldecode ($1); }
if (/^email=(.*)/) { $email=&urldecode ($1); }
}
#Проверяем,не существует ли данное имя в системе.
open(INFO,"login.txt") ||die;
@data=<INFO>;#Читаем строки в массив.
close(INFO);
foreach $string(@data) {
@item=split(/&/,$string);#Разбиваем строку на части.
foreach (@item) {
if ($item[0] eq $login) { #Сравниваем полученное имя с первым полем файла
#для каждой строки и если такое найдено выдаем
#ошибку.
print <<HTML;
<h2 align=center><font color="ff0000">Error!</font></h2>
<p><center><b>The name <font color="ff0000">$login</font> already exists in the system.
<p>Please,go back and choose another name.</b>
<p><form><input type="button" value="Back" onClick="history.back()"></form>
</center>
HTML
exit;
}
}
}
#Если имя не найдено,открываем базу данных и добавляем информацию.
if ($item[0] ne $login) {
open(DATA,">>login.txt");
$string=join('&',$login,$password,$email,scalar localtime,$ENV {'REMOTE_ADDR'};
print DATA "$string\n";
close(DATA);
#Создаем домашний каталог пользователя и переходим в него.
mkdir("$userdir/$login",0700);
chdir("$userdir/login");
opendir(USER,"$userdir/$login");
#Помещаем файл index.html в каталог пользователя.
open(IN,">$userdir/$login/index.html");
print IN "This is the test!\n";
close(IN);
closedir(USER);
#Содержание файла может быть любым,это только для примера.
#Генерируем ответ пользователю.
print <<HTML;
<p><h1 align=center><font color="ff0000">Congratulations!</font></h1>
<p><b>Your registration was successful and your data were added to
our database.Thank you for your time.</b>
<p><center><b><font color="ff0000">
You entered:</font>(print this page and keep it in safe place)</b>
<p><table>
<tr><td><b>Your login name:</b><td><font color="0000ff">$login</font>
<tr><td><b>Your password:</b><td><font color="0000ff">$password</font>
<tr><td><b>Your e-mail address:</b><td><font color="0000ff">$email</font>
</table></center>
HTML
}
}
Скрипт выдает ответ в виде html-страницы,содержащей всю информацию,введенную пользователем.
Пример 2.
Рассмотрим пример открытия и чтения каталога и вывод списка файлов,содержащихся в нем.Начало скрипта можно взять из предыдущего примера.Предположим,что пользователь,зарегистрированный на веб-сервере,хочет войти в свой домашний каталог.
#!/usr/local/bin/perl
#Объявляем глобальные переменные.
$request=$ENV{'REQUEST_METHOD'};
$content=$ENV{'CONTENT_LENGTH'};
$basedir="//www.mydomain.com/~";
$file="login.txt";
$url="//www.mydomain.com";
$dir="f:/home/";
$cgi="f:/usr/local/apache/cgi-bin";
#Подпрограммы для декодирования данных из формы.
sub urldecode {
local($val)=@_;
$val=~ s/\+/ /g;
$val=~ s/%[0-9a-hA-H] {2}/pack('C',hex($1))/ge;
return $val;
}
sub strhtml {
local($val)=@_;
$val=~s//>/g;
$val=~s/(\/\/\+S)/<A href="$1">$1<\/A>/g;
return $val;
}
######################################################################
if ($request eq 'GET') {
$query=$ENV{'QUERY_STRING'};
}
else {
sysread(STDIN,$query,$content);
}
#Генерируем форму,если никакие данные не введены.
print "Content-type:text/html\n\n";
print <<HTML_gen;
<HTML><BODY bgcolor="e6e8fa">
HTML_gen
if ($query eq '') {
print "Content-type:text/html\n\n";
print <<HTML;
<HTML><HEAD>
</HEAD><BODY bgcolor="e6e8fa">
<FORM ACTION="../cgi-bin/fileman.cgi" name="form1" METHOD="POST">
<h2 align=center><font color="ff0000">System login.</font></h2>
<p><center>Please,enter your login name and password:
<p><TABLE BGCOLOR="cccccc">
<tr><td colspan=2 align=center bgcolor="99cccc"><b><font color="ff0000">
I am registered user</font></b>
<TR><TD><p><b>Login:</b><TD><INPUT TYPE="text" NAME="login" SIZE="20">
<TR><TD><p><b>Password:</b><TD><INPUT TYPE="password" NAME="pass" SIZE="20">
<tr><td colspan=2 align=center><input type=submit value="Submit"></center>
HTML
}
#Если информация получена,декодируем поля формы.
else {
foreach (@fields=split(/&/,$query)) {
if (/^login=(.*)/) { $login=&urldecode ($1); }
if (/^pass=(.*)/) { $password=&urldecode ($1); }
}
#Открываем базу данных и проверяем логин и пароль.
open(INFO,$file) ||die;
@data=;
close(INFO);
foreach $string(@data) {
@item=split(/&/,$string);
foreach (@item) {
if (($item[0] eq $login) && ($item[1] eq $password)) {
#Если все нормально,переходим в пользовательский каталог.
print "Content-type:text/html\n\n";
print <<HTML;
<html><body bgcolor="e6e8fa">
#Приветствуем пользователя.
<p><h2 align=center><font color="ff0000">Hello,$login!</font></h2>
<p><center>Welcome to your home directory!
<p>Your URL is <a href="$basedir$login">$basedir$login.</a></center>
HTML
#########################
# Directories list #
#########################
$userdir=$dir.$login;
chdir ("$userdir");
#Открываем каталог и читаем список файлов в массив.
opendir(DIR,"$userdir") || die "Cannot open $userdir!";
while (@files=readdir(DIR)) {
#Если каталог содержит подкаталоги,выводим их отдельно,а также не показываем
#каталоги "." и ".." Печатаем шапку таблицы.
print <<HTML;
<p><center>
<table bgcolor=\"bfbfbf\" width=600 border cellspacing=0 cellpadding=0 nowrap>
<tr><td colspan=5 align=center nowrap><b><font color="ff0000">Directories</font></b></td></tr>
<tr><td>.</td><td align=center><b>List</b></td><td><b>Size</b><td><td><b>Last accessed</b></td><td><b>Last modified</b></td>
HTML
foreach $file(@files) {
#Стстистика файлов-размер,время последнего обращения и модификации.
$size=(stat("$userdir/$file"))[7];
$atime=localtime((stat("$userdir/$file"))[8]);
$mtime=localtime((stat("$userdir/$file"))[9]);
#Печатаем список подкаталогов.
if ( -d "$userdir/$file" && "$file" ne "." && "$file" ne "..") {
print "<tr><td width=30><img src=\"$url/image/folder.gif\"></td><td width=100 align=left>$file</td>\n";
print "<td width=50>",$size,"</td><td width=200>",$atime,"</td><td width=200>",$mtime,"</td></tr>\n";
}
}
print "</table>\n";
################
# Files list #
################
#Ту же операцию проводим для файлов.Печатаем шапку таблицы.
print <<HTML;
<p><table bgcolor=\"bfbfbf\" width=600 border cellspacing=0 cellpadding=0>
<tr><td colspan=5 align=center><b><font color="ff0000">Files</font></b></td></tr>
<tr><td>.</td><td><b>List</b><td><b>Size</b><td><b>Last accessed</b><td><b>Last modified</b></tr>
HTML
foreach $file(@files) {
$size=(stat("$userdir/$file"))[7];
$atime=localtime((stat("$userdir/$file"))[8]);
$mtime=localtime((stat("$userdir/$file"))[9]);
if (!-d "$userdir/$file" && "$file" ne "." && "$file" ne "..") {
push (@dir,"$userdir/$file");#Помещаем найденные файлы в массив
$number=@dir; #Подсчитываем их количество.
#Выдаем информацию.
print "<tr><td width=30><img src=\"$url/image/page.gif\"></td><td width=100><a href=\"$basedir$login/$file\">",$file,"</a></td>\n";
print "<td width=50>",$size ,"</td>\n";
print "<td width=200>",$atime,"</td><td width=200>",$mtime,"</td></tr>\n";
}
}
print "</table>\n";
print "<p><center><b><font color=\"0000ff\">There are ",$number," files in this directory.</b></font></center>\n";
}
Надеюсь,я объяснил все достаточно подробно.Я выбрал намеренно сложные примеры,чтобы
показать все операции,которые можно производить с файлами и каталогами.Файлы еще можно
загружать на сервер через веб.Этому посвящен следующий раздел.
Загрузка файлов на сервер через Интернет.
Файлы можно загружать на веб-сервер через Интернет,используя формы.Вы,наверное,сами не раз
это делали.Разберем более подробно,как это делается.
Нужно создать форму с полем типа file и методом кодировки multipart/form-data.
#!/usr/local/bin/perl
print "Content-type:text/html\n\n";
print <<HTML;
<html><head>
<script language="javascript">
<!--
function fill () {
if (fn==document.form.entry.value) {
document.form.file.value=fn; }
}
//-->
</script>
</head>
<body bgcolor="e6e8fa">
HTML
print "<p><table width=300 bgcolor=\"bfbfbf\">\n";
print "<h3 align=center><font color=\"0000ff\">File upload:</font></h3>\n";
print "<center><FORM action=\"../cgi-bin/upload.cgi\" name=\"form\"
METHOD=\"POST\" ENCTYPE=\"multipart/form-data\">\n";
print "<tr><td align=center><b>Select file:</b></td>\n";
print "<tr><td><input type=\"file\" name=\"entry\" onBlur=\"fill()\"></td>\n";
print "<tr><td><input type=\"hidden\" name=\"file\" value=\"1\"></td>\n";
print "<tr><td align=center><input type=\"Submit\" value=\"Upload\"></td></table>\n";
print "</form></center></table></body></html>\n";
Функция Javascript использована для того,чтобы передать на сервер имя загружаемого файла,
включая полный путь.Далее,скрипт декодирует его,отбросит путь и загрузит файл под его именем.
Функцию для декодирования в этом случае я использую готовую,нашел в Интернете,за что большое спасибо ее разработчику.
$content_type = $ENV{'CONTENT_TYPE'};
binmode STDIN;
read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'});
if ((!$content_type) || ($content_type =~ m#^multipart/form-data#)){
($boundary = $content_type) =~ s/^.*boundary=(.*)$/\1/;
@pairs = split(/--$boundary/, $buffer);
@pairs = splice(@pairs,1,$#pairs-1);
for $part (@pairs)
{
($dump,$fline,$value) = split(/\r\n/,$part,3);
next if $fline =~ /filename=\"\"/;
$fline =~ s/^Content-Disposition: form-data; //;
(@columns) = split(/;\s+/, $fline);
($name = $columns[0]) =~ s/^name="([^"]+)"$/\1/g;
if ($#columns > 0)
{
if ($value =~ /^Content-Type:/)
{
($dump,$dump,$value) = split(/\r\n/,$value,3);
}
else {($dump,$value) = split(/\r\n/,$value,2);}}
else {($dump,$value) = split(/\r\n/,$value,2);
if (grep(/^$name$/, keys(%CGI))) {
if (@{$FORM{$name}} > 0) {
push(@{$FORM{$name}}, $value);}
else {
$arrvalue = $FORM{$name};
undef $FORM{$name};
$FORM{$name}[0] = $arrvalue;
push(@{$FORM{$name}}, $value);}}
else {
next if $value =~ /^\s*$/;
$FORM{$name} = $value;}
next;}
$FORM{$name} = $value;}}
Как видите,довольно сложная и громоздкая,зато загрузка проходит без проблем.Далее нужно получить имя файла и отбросить путь,оставив только имя.
$upfile=$FORM {'entry'}; #Имя загружаемого файла.
$destfile=$FORM {'file'}; #Имя,под которым он будет записан в каталог назначения.
$destdir="/home/upload"; #Имя каталога для загрузки.
chdir ("$destdir");
#Отбрасываем путь,оставляя только имя.
$destfile=~s/\w+//;
$destfile=~s/([^\/\\]+)$//;
$destfile=$1;
#Далее записываем файл в каталог назначения.
open(FILE, ">$destdir/$destfile"); #Открываем на запись новый файл.
binmode FILE; #Устанавливаем бинарный режим.
print FILE $upfile; #Записываем в него содержимое загруженного файла.
close(FILE); #Закрываем файл.
Все,загрузка завершена.Таким способом можно загружать сразу несколько файлов-5 или 10,создав для каждого элемент формы и,само собой,добавив в скрипте нужное количество обработчиков.
|