MySQL / 17. Внедрение результатов запросов в веб-страницы

Извлечение изображений и других двоичных данных

Задача
Отлично, мы научились сохранять изображения и другие двоичные данные в базе данных. Но как теперь получить их обратно?

Решение
Нужно всего лишь выполнить предложение SELECT. Естественно, ответить на вопрос о том, что делать с этой информацией после извлечения, несколько сложнее.

Обсуждение
Было бы тяжело вручную задавать буквальное значение, представляющее собой изображение. Однако ввод запроса, извлекающего изображения, не создает никаких трудностей:

mysql> SELECT * FROM image WHERE id = 1;

Но двоичная информация часто не очень удачно выводится на устройствах ASCII, поэтому, вероятно, вы не захотите осуществлять интерактивный вывод из программы mysql, чтобы терминальное окно не заполнилось мусором и, тем более, не заблокировалось. Наиболее часто информацию используют для отображения на веб-странице. Или можно отправить ее клиенту для загрузки (хотя такой способ чаще применяется для двоичных данных, не являющихся изображениями, таких как файлы PDF).

Вывод изображения на веб-страницу реализуется при помощи тега <img>, который сообщает броузеру клиента, где взять изображение. Если вы храните изображения в виде файлов в каталоге, к которому веб-сервер имеет доступ, то можете ссылаться на него напрямую. Например, если файл изображенияiceland.jpg расположен в каталоге /mcb/images корневого каталога документов сервера, то вы можете сослаться на него так:

<img src="/mcb/images/iceland.jpg" />

Если вы применяете этот подход, проверьте, все ли файлы изображений имеют расширение (например, .gif или .png), которое позволит веб-серверу определить, какой тип заголовка Content-Type: следует отправлять в ответе на запрос. Если изображения хранятся в базе данных или в каталоге, недоступном для веб-сервера, то тег <img> может ссылаться на сценарий, который знает, как извлекать изображения и отправлять их клиенту. Для этого сценарий должен отправлять ответ, содержащий заголовок Content-Type: для указания формата изображения, заголовок Content-Length:, сообщающий о количестве байт в изображении, пустую строку и, наконец, само изображение как тело ответа.

Сценарий display_image.pl показывает, как отправлять изображения через Сеть. Необходимо задать параметр name, указывающий на то, какое изобра жение следует вывести, кроме того, можно задать необязательный параметр location, показывающий, откуда нужно извлекать изображение: из таблицы image или из файловой системы. По умолчанию данные извлекаются из таблицы image. Например, следующие URL выводят изображение из базы данных и из файловой системы соответственно:

http://apache.snake.net/cgi-bin/display_image.pl?name=iceland.jpg
http://apache.snake.net/cgi-bin/display_image.pl?name=
iceland.jpg;location=fs

Сценарий выглядит так:

#! /usr/bin/perl -w
# display_image.pl – отправить изображение через веб
use strict;
use lib qw(/usr/local/apache/lib/perl);
use CGI qw(:standard escapeHTML);
use FileHandle;
use Cookbook;
# Значения по умолчанию для каталога хранения изображений и разделителя пути
# (CHANGE THESE AS NECESSARY)
my $image_dir = "/usr/local/apache/htdocs/mcb/images";
my $path_sep = "/";
# Установить каталог и разделитель пути для Windows/DOS
if ($^O =~ /^MSWin/i || $^O =~ /^dos/)
{
$image_dir = "D:\\apache\\htdocs\\mcb\\images";
$path_sep = "\\";
}
my $name = param ("name");
my $location = param ("location");
# убедиться в том, что имя изображения было указано
defined ($name) or error ("image name is missing");
# использовать умолчание - "db", если местоположение не указано, или не "db" и не "fs"
(defined ($location) && $location eq "fs") or $location = "db";
my $dbh = Cookbook::connect ();
my ($type, $data);
# Если местоположение - "db", получить данные и MIME-тип из таблицы image.
# Если местоположение - "fs", получить MIME-тип из таблицы image,
# а данные считать из файловой системы.
if ($location eq "db")
{
($type, $data) = $dbh->selectrow_array (
"SELECT type, data FROM image WHERE name = ?",undef,
$name)
or error ("Cannot find image with name $name");
}
else
{
$type = $dbh->selectrow_array (
"SELECT type FROM image WHERE name = ?",
undef,
$name)
or error ("Cannot find image with name $name");
my $fh = new FileHandle;
my $image_path = $image_dir . $path_sep . $name;
open ($fh, $image_path)
or error ("Cannot read $image_path: $!");
binmode ($fh); # для работы в двоичном режиме
my $size = (stat ($fh));
read ($fh, $data, $size) == $size
or error ("Failed to read entire file $image_path: $!");
$fh->close ();
}
$dbh->disconnect ();
# Отправить изображение клиенту с заголовками Content-Type: и Content-Length:.
print header (-type => $type, -Content_Length => length ($data));
print $data;
exit (0);
# ----------------------------------------------------------------------
sub error
{
my ($msg) = shift;
print header (), start_html ("Error"), p (escapeHTML ($msg)), end_html ();
exit (0);
}

Статьи по MySQL на эту тему:

Использование результатов запроса для загрузки файлов
Работа с баннерами