Rambler's Top100
"Knowledge itself is power"
F.Bacon
Поиск | Карта сайта | Помощь | О проекте | ТТХ  
 Hello, World!
  
 

Фильтр по датам

 
 К н и г и
 
Книжная полка
 
 
Библиотека
 
  
  
 


Поиск
 
Поиск по КС
Поиск в статьях
Яndex© + Google©
Поиск книг

 
  
Тематический каталог
Все манускрипты

 
  
Карта VCL
ОШИБКИ
Сообщения системы

 
Форумы
 
Круглый стол
Новые вопросы

 
  
Базарная площадь
Городская площадь

 
   
С Л С

 
Летопись
 
Королевские Хроники
Рыцарский Зал
Глас народа!

 
  
ТТХ
Конкурсы
Королевская клюква

 
Разделы
 
Hello, World!
Лицей

Квинтана

 
  
Сокровищница
Подземелье Магов
Подводные камни
Свитки

 
  
Школа ОБЕРОНА

 
  
Арсенальная башня
Фолианты
Полигон

 
  
Книга Песка
Дальние земли

 
  
АРХИВЫ

 
 

Сейчас на сайте присутствуют:
 
  
 
Во Флориде и в Королевстве сейчас  00:03[Войти] | [Зарегистрироваться]

Простые CGI приложения на Дельфи. Просто рисуем свой счетчик

Елена Филиппова
дата публикации 16-10-2000 00:00

Простые CGI приложения на Дельфи. Просто рисуем свой счетчик

В продолжение статьи Алексеея Еремеева "Простые CGI-приложения на Delphi" я попробую рассказать о генерации изображений "на лету" на примере счетчика посещений.

Материал этот предназначен для начинающих программистов, тем не менее предполагается, что перед его прочтением вы познакомились со статьей Алексея Еремеева "Простые cgi-приложения на Delphi" и материалом Евгения Акованцева "К вопросу о построении гостевой книги" из раздела "Hello, Word!". А так же пытались разобраться с методами и свойствами класса TWebModule.

Генерация изображения "на лету"

Смысл этой красивой фразы довольно прост - требуется вывести на HTML-странице картинку, которой нет. То есть просто нарисовать ее перед тем, как сервер сгенерит страницу по запросу клиента.
Области применения этой технологии - вывод некой динамической информации в виде графического изображения. Например, счетчик посещений сайта, всевозможные статистические графики, рейтинги и наградные(именные) маркеры.
Начнем с самого простого - счетчика посещений.
Практически каждый сайт, посвященный web-программированию, содержит пример такого счетчика. Алгоритм его создания довольно прост:
  • Есть текущее значение счетчика;
  • При каждой загрузке скрипта оно увеличивается на единицу и формируется GIF-изображение ( то есть рисуются цифры);
  • Картинка выводится в выходной поток и отправляется клиенту.

Можно было бы не повторяться, но некоторые моменты в самых распространенных в сети примерах, мягко говоря не совсем корректны.

Тех, кто крайне отрицательно относится к лирическим отступлениям, просьба следующий раздел не читать, дабы не раздражаться лишний раз.

Где хранить значение счетчика?

Вопрос, который не имеет никакого отношения к генерации изображений, но ответить на который надо, пржде чем писать такой скрипт.
Чаще всего предлагается использовать для хранения текущего значения счетчика таблицу в базе данных. Заводите табличку с соответствующим полем и апдейтите его при каждой загрузке скрипта. Дешево и сердито.
Тут даже не важно, есть ли у вас возможность использовать БД или нет, здесь важен сам подход. Базу данных надо использовать тогда, когда есть необходимость в выборках по различным условиям, поиске и сортировке. Что вряд ли требуется для функционирования счетчика посещений, даже с минимальной статистикой. Не все то, что на первый взгляд "проще" по реализации, на самом деле лучшее решение.
В полном соответствии с принципом "бритвы Оккама".

В 13-ом веке в Англии жил философ Уильям Оккам (ударение на первый слог). Занятия его философией и теологией для большей части программистов покрыты мраком, но имя его известно, по крайней мере для любителей фантастики, благодаря знаменитому принципу "бритвы Оккама". Принцип этот гласит - "Отсекай все лишнее" или "Не вводите новых сущностей без особой надобности".

Стоит ли вводить новую сущность (БД) между клиентом и сервером в этом случае? Ведь чем длиннее цепочка, тем больше шансов у нее порваться :о)

Послушаемся мудрого Оккама, не будем тревожить БД по мелочам и будем хранить текущее значение счетчика в файле.

Пишем и читаем

Важно ! Для того, чтобы писать в файл на диске на Вашем Web-сервере, необходимо, чтобы соответствующий каталог имел атрибут Change.

В нашем примере функция ReadCounter возвращает текущее значение счетчика, читая его из файла. При этом, если файла нет на диске, она его создает и устанавливает значение счетчика в 1. Такми образом нет необходимости специально создавать файл на диске с нужным именем и начальным значением. Функция все сделает сама.
Формат файла может быть текстовый или бинарный. Разницы нет никакой, кроме возможности (или невозможности) поправлять значение счетчика "ручками".
Я буду использовать бинарный файл. Если Вам захочется переделать код на работу с текстовым файлом, смотрите Help, функция AssignFile и далее по ссылкам.

Function ReadCounter : String;
Var   FHandle     ,
      Counter     : Cardinal;
      FileName    : String;
Begin
    Result:= '0';
    // имя файла со значением счетчика
    FileName:='counter.dat';

    Counter:=0;

    Try
	// пытаемся открыть или создать этот файл и читать из него	
      IF NOT FileExists(FileName)
      Then FHandle:=FileCreate(FileName)
      Else Begin
             FHandle:=FileOpen(FileName,fmOpenReadWrite);
             FileRead(FHandle,Counter,SizeOF(Counter));
           End;

      Result:= IntToStr(Counter);
	// увеличиваем счетчик 		  
      Inc(Counter);

	// и пытаемся записать новое значение обратно в файл 		  	  
      FileSeek(FHandle,0,0);
      FileWrite(FHandle,Counter,SizeOF(Counter));
      FileClose(FHandle);
    Except
    End;
   

End;

Функция фозвращает сразу символьное значение счетчика, так удобнее для ее дальнейшего использования. Тип Cardinal это беззнаковое 32-битное целое число, его диапазон - < 0 .. 4 294 967 295 > , так что любителям накручивать счетчики будет где развернуться :о)

GIF или JPEG ?

Выводить картинку можно в одном из этих форматов, но в каком лучше? С точки зрения программирования разницы нет никакой, ну кроме того, что будут использоваться разные объекты. Предпочтение конкретному формату зависит от Ваших личных вкусов и типа изображения. Дело в том, что формат GIF лучше подходит для четких изображений (надпись, график и т.п.), а формат JPEG - для картинок с плавными переходами цветов.
Так что , можете поэкспериментировать и решить, что больше нравится именно Вам.
В нашем примере будем рисовать GIF, для этого нам необходим соответствующий класс, который поддерживает формат GIF.
В сети есть немало свободно распространяемых графических компонент, я буду использовать в своем примере RXGIF. Не потому, что он лучше для этого подходит (скорее всего наоборот, он тяжеловат, так как реализует много возможностей, которые нам даже и не пригодятся ) , но у меня он уже есть и искать специально что-то другое только для этого примера нет надобности.

Рисуем счетчик

Итак, приступим. Создаем новое приложение:
File-> New -> Web Server Application -> CGI Stand Alone Executable.
В WebModule в Actions добавляем новое действие (Add Item).
Устанавливаем для него Default:=True. В обработчике этого действия (WebActionItem) и пишем нужный нам код.
Смысл этих действий более подробно описан в материале "К вопросу о создании гостевой книги".

Итак, алгоритм следующий:
  1. Читаем из файла значение счетчика;
  2. Создаем объект TBitmap и пишем, то есть рисуем на его канве текст;
  3. Создаем объект TGIFImage и передаем ему изображение, то есть конвертируем в формат GIF сформированную картинку. Это действие необходимо, так как сразу рисовать в формате GIF мы не можем;
  4. Создаем объект TMemoryStream и "переливаем" в бинарный поток наше изображение;
  5. Передаем этот поток в Response и отправляем его в броузер клиента.
  6. Ну и не забываем почистить за собой.
procedure TWebModule1.WebModule1WebActionItem1Action(Sender: TObject;
  Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
Var
   Image      : TGIFImage;
   Stream     : TMemoryStream;
   BMP        : TBitMap;
begin
   Image:=TGIFImage.Create;
   BMP:=TBitMap.Create;
   Stream:=TMemoryStream.Create;

   Try 	   
   	BMP.Width:=100;
   	BMP.Height:=20;

   	BMP.Canvas.TextOut(0,0, ReadCounter);

   	Image.Assign(BMP);
   	Image.SaveToStream(Stream);
   	Stream.Position:=0;
	Try
   		Response.ContentType:='image/gif';

   		Response.ContentStream:=Stream;
   		Response.SendResponse;
   	Except
		Stream.Free;
   	End;
   Finally
   	Image.Free;
   	BMP.Free;   
   End; 

end;

Теперь при каждом обращении к адресу http://localhost/cgi-bin/counter.exe , в уголке броузера будет выводится новое значение счетчика. Можно вcтавлять этот вызов в свои html-страницы.

Примечание:

Кого-то может заинтересовать , отчего объекты Image и BMP я освобождаю в любом случае, а Stream только в случае неудачного завершения персылки данных клиенту?
Дело в том, что сразу после успешной отработки метода Response.SendResponse объект Stream окажется ... уже разрушенным.
И попытка освободить его еще раз приведёт к Access Violation. Можете поэкспериментировать сами.

Передача параметров

Цель примера - показать в общем эту технологию, но оставлять этот код как образец счетчика нельзя, больно он убого выглядит.
Стоило возиться с графикой, чтобы просто "накарябать цифирьку"?..
Усложним нашу задачу. Сделаем более универсальный счетчик с настраиваемыми параметрами.
Прежде чем наводить красоту, нужно решить, как мы будем задавать всякие разные параметры.
Внимательно прочитайте Help по теме TWebRequest, нас будет интересовать свойство QueryFields : TStrings. Это список параметров запроса по методу GET, то есть тех параметров, которые передаются в теле строки URL.
Например, запрос
http://localhost/cgi-bin/counter.exe?par1=10&par2=Test& 
передаст на сервер список из двух строк: 'par1=10' и 'par2=Test'

Доступ к списку имен параметров и их значений предоставляют свойства класса TStrings :
property Names[Index: Integer]: string; 
и
property Values[const Name: string]: string; 
соответственно.

И если нас интересует входной параметр Par, его значение можно получить из Request.QueryFields.Values['Par'], если такого параметра в строке запроса не было, в качестве его значения получим пустую строку .
Данные, передаваемые клиенту методом POST (поля формы), будут доступны через TWebRequest.ContentFields.

Для того, чтобы получать нужные нам значения независимо от способа передачи параметров, напишем вот такую функцию:


//------------------------------------------------------------
//      Получить входящие данные независимо от метода запроса
//
//      Порядок поиска : 1) Из входящего потока (GET )
//                       2) Из полей формы      (POST)
//                       3) Из cookies
//------------------------------------------------------------
Function GetRequest( Request : TWebRequest ; Value : String) : String;
Begin
    Result:=Request.QueryFields.Values[Value];

    Result:=IsEmpty( Result , Request.ContentFields.Values[Value]);
    Result:=IsEmpty( Result , Request.CookieFields.Values[Value]);

    Result:=Trim(Result);
End;
//------------------------------------------------------------
Function IsEmpty( Value , Empty : String): String;
Begin
    IF Trim(Value) = '' Then Value:=Empty;
    Result:=Value;
End;
//------------------------------------------------------------
Для счетчика это не так важно, но в дальнейшем такая функция пригодится.

Красота - это страшная сила!

А теперь можно и красоту наводить. Что бы такого придумать полезного?
Во-первых, заставим скрипт читать значение счетчика из разных файлов. Например будем передавать ему в качестве параметра номер счетчика. Тогда можно будет использовать один и тот же скрипт на разных страницах, передавая ему разные номера (параметр Data) .
Во-вторых, введем такое понятие, как разрядность счетчика, вместо "162" будем выводить "000162", так выглядит интереснее. И саму разрядность будем передавать по параметру (параметр NoCount).
И, наконец, просто раскрасим его, читая из входного потока и цвет (параметр Color).

Для защищенной работы скрипта необходимо предусмотреть систему умолчаний, на случай если параметры будут не заданы или неверно заданы.
Например вот так:
   Text:=GetRequest(Request,'Color');
   IF NOT IdentToColor( Text , Color )  Then Color:=clWhite;

   No:=GetNumeric(GetRequest(Request , 'NoCount');
   IF No = 0 Then No:=10;
Здесь использована функция GetNumeric, которая возвращает целое число вместо строки символов. Ее реализацию смотрите в коде примера.

Вот так изменится код формирования изображения:
// Устанавливаем параметры шрифта, в том числе полученный цвет 
BMP.Canvas.Font.Color:=TColor(Color);
BMP.Canvas.Font.Size:=12;
BMP.Canvas.Font.Name:='Arial';
BMP.Canvas.Font.Style:=BMP.Canvas.Font.Style + [fsBold];

// Рассчитываем размер картинки исходя из выбранного шрифта
// ну и прибавляем по пикселю со всех сторон, для красоты :о)  
CharW:=BMP.Canvas.TextWidth('0');
BMP.Width:=No*CharW + 2;
BMP.Height:=BMP.Canvas.TextHeight('0') + 2;

// Готовим фон счетчика и рисуем его!
BMP.Canvas.Brush.Color:=ClBlack;
BMP.Canvas.FillRect(RECT(0,0 , BMP.Width,BMP.Height));
BMP.Canvas.TextOut(1 ,1 , ReadCounter(No , FileName));

Вот так будет выглядеть наш счетчик, если никаких параметов не задавать вообще.
http://localhost/cgi-bin/counter.exe

А вот так с параметрами.
http://localhost/cgi-bin/counter.exe?NoCount=5&Color=clAqua

Красота это страшная сила...

Итого:

Естественно, что после таких наших нововведений и функция ReadCounter и обработчик события OnAction изменились. Приводить их здесь полностью нет смысла, скачайте пример и поэкспериментируйте с кодом. Это будет удобнее и полезнее.

Вы еще не обратили внимание на размер исполняемого кода нашего скрипта?
Ничего не поделаешь.. за удобство надо платить.

Если переписать этот счетчик в стиле Алексея Еремеева, размер исполняемого кода сократится в два раза :о)

Елена Филиппова
Специально для Королевства "Delphi"



К материалу прилагаются файлы:


Смотрите также материалы по темам:
[TWebModule] [CGI]

 Обсуждение материала [ 10-01-2005 18:28 ] 7 сообщений
  
Время на сайте: GMT минус 5 часов

Если вы заметили орфографическую ошибку на этой странице, просто выделите ошибку мышью и нажмите Ctrl+Enter.
Функция может не работать в некоторых версиях броузеров.

Web hosting for this web site provided by DotNetPark (ASP.NET, SharePoint, MS SQL hosting)  
Software for IIS, Hyper-V, MS SQL. Tools for Windows server administrators. Server migration utilities  

 
© При использовании любых материалов «Королевства Delphi» необходимо указывать источник информации. Перепечатка авторских статей возможна только при согласии всех авторов и администрации сайта.
Все используемые на сайте торговые марки являются собственностью их производителей.

Яндекс цитирования