Встраивание графики в веб-страницу
180ASP.NET --- Веб-сайты ASP.NET --- Встраивание графики в веб-страницу
Применение метода Image.Save() для записи изображения в поток ответа ведет к перезаписи любой информации, которую среда ASP.NET использовала бы в противном случае. К счастью, существует более простое решение. Можно воспользоваться HTML-дескриптором <img> или веб-элементом управления Image, но вместо указания статического изображения в качестве источника понадобится выполнить привязку к файлу .aspx, который генерирует динамическое изображение.
Например, рассмотрим любое графическое изображение, созданное средствами GDI+ в предыдущей статье. Оно хранится в файле SimpleDrawing.aspx и записывает динамически сгенерированное изображение в поток ответа (в приведенных примерах в файловый поток). Вывести динамическое изображение на другой странице можно было бы за счет добавления к ней веб-элемента управления Image и установки SimpleDrawing.aspx в качестве значения свойства ImageUrl (либо сгенерированного файла). Затем можно было бы добавить другие элементы управления или даже несколько элементов управления Image, которые устанавливают связь с этим же содержимым.
На рисунке ниже показан пример, в котором используются два дескриптора <img>, указывающие на файл SimpleDrawing.aspx, а также ряд дополнительных веб-элементов управления ASP.NET. расположенных между ними:
<body>
<form id="form1" runat="server">
<asp:Image ID="Image1" runat="server" ImageUrl="~/SimpleDrawing.aspx" />
<br /><br />
<asp:Button ID="Button1" runat="server" Text="Button" />
<br /><br />
<asp:Label ID="Label1" runat="server" Text="Label" />
<br /><br />
<asp:Image ID="Image2" runat="server" ImageUrl="~/SimpleDrawing.aspx" />
</form>
</body>
Помните, что обычно создание рисунка GDI+ выполняется на порядок медленнее, чем обслуживание статического изображения. Поэтому вряд ли имеет смысл строить графические кнопки и другие многократно повторяющиеся на странице элементы с помощью GDI+. (Если все же решитесь на это, подумайте о кэшировании или сохранении файла изображения сразу после его генерации для повышения производительности.)
Использование формата PNG
PNG является универсальным форматом, который всегда обеспечивает высокое качество, сочетая в себе сжатие без потерь GIF-изображений с широкими возможностями цветовой поддержки JPEG. Некоторые браузеры (особенно более старые версии Internet Explorer) неправильно выводят на экран PNG-изображения при их динамическом возвращении из страницы. Вместо содержимого рисунка пользователь получает сообщение с предложением загрузить содержимое рисунка и открыть его в другой программе. Для решения этой проблемы можно применить ранее рассмотренный подход с дескриптором <img>.
Еще одна проблема, связанная с динамической генерацией PNG-изображений - невозможность использования метода Bitmap.Save(), рассмотренного в предшествующей статье. Response.OutputStream является линейным потоком, т.е. данные должны записываться последовательно с начала до конца. Чтобы создать PNG-файл, программные средства .NET должны иметь возможность перемещаться по файлу назад и вперед, что требует потока, который может обеспечивать переход в конкретные позиции.
Решение достаточно просто. Вместо того чтобы выполнять сохранение непосредственно в поток Response.OutputStream, нужно создать поток System.IO.MemoryStream, который представляет собой буфер данных в памяти. Вызовите Bitmap.Save(), чтобы сохранить изображение в поток MemoryStream, а затем запишите MemoryStream в поток Response.OutputStream.
Код, необходимый для реализации этого решения, при условии, что пространство имен System.IO было импортировано, имеет следующий вид:
protected void Page_Load(object sender, EventArgs e)
{
using (Bitmap image = new Bitmap(450, 100))
{
using (Graphics graphic = Graphics.FromImage(image))
{
/*
* Какой-то код, генерирующий изображение
*/
// Сохранить изображение в поток
Response.ContentType = "image/png";
// Создать PNG-изображение, хранящееся в памяти
MemoryStream mem = new MemoryStream();
image.Save(mem, System.Drawing.Imaging.ImageFormat.Png);
// Записать данные MemoryStream в выходной поток
mem.WriteTo(Response.OutputStream);
}
}
}
Передача информации динамическим изображениям
При генерации графики в веб-страницах информацию можно отправлять из страницы коду, который собственно генерирует графику, для создания по-настоящему динамических изображений. В следующем примере создается привязанный к данным список, который отображает эскиз каждого растрового изображения, хранящегося в данном каталоге. Конечный результат показан на рисунке ниже:
Эта страница должна состоять из двух частей: страницы, которая содержит элемент управления GridView, и страницы, которая динамически визуализирует одиночный эскиз. Для заполнения списка страница GridView будет многократно вызывать страницу эскиза (используя дескрипторы <img>).
Имеет смысл сначала разработать страницу, создающую эскиз. В этом примере страница названа ThumbailsViewer.aspx. Чтобы сделать этот компонент максимально универсальным, не следует жестко кодировать какую-либо информацию об используемом каталоге или размерах эскиза. Вместо этого данная информация будет получена с помощью трех строковых аргументов запроса.
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
public partial class ThumbailsViewer : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if ((String.IsNullOrEmpty(Request.QueryString["X"])) ||
(String.IsNullOrEmpty(Request.QueryString["Y"])) ||
(String.IsNullOrEmpty(Request.QueryString["FilePath"])))
{
// Часть данных отсутствует, поэтому ничего не выводить на экран.
// Другие возможные варианты действий - выбор подходящих значений по умолчанию
// или возврат изображения со статическим текстом сообщения об ошибке
}
else
{
int x = Int32.Parse(Request.QueryString["X"]);
int y = Int32.Parse(Request.QueryString["Y"]);
string file = Server.UrlDecode(Request.QueryString["FilePath"]);
// Создать хранящееся в памяти растровое изображение,
// где будет выполняться рисование
using (Bitmap image = new Bitmap(x, y))
{
using (Graphics graphic = Graphics.FromImage(image))
{
// Загрузить данные из файла
System.Drawing.Image thumbnail =
System.Drawing.Image.FromFile(file);
// Нарисовать эскиз
graphic.DrawImage(thumbnail, 0, 0, x, y);
// Сохранить изображение
image.Save(Response.OutputStream, ImageFormat.Jpeg);
}
}
}
}
}
При первой загрузке страницы необходимо проверить, что вся эта информация предоставлена. Как только основной набор данных получен, объекты Bitmap и Graphics можно создать обычным образом. В данном случае размеры объекта Bitmap должны соответствовать размеру эскиза, поскольку добавлять какое-то дополнительное содержимое не требуется. Создание эскиза не представляет особой сложности. Достаточно загрузить изображение (с использованием статического метода Image.FromFile), а затем вывести его на поверхность рисования. При рисовании изображения нужно указать начальную точку (0, 0), а также высоту и ширину. Высота и ширина соответствуют размерам объекта Bitmap. Класс Graphics автоматически масштабирует изображение в соответствии с этими размерами, применяя сглаживание для создания высококачественного эскиза.
Следующий необходимый шаг - применение этой страницы в странице, которая содержит элемент управления GridView. Основная идея, лежащая в основе базовой страницы, заключается в том, что пользователь будет вводить путь к каталогу и щелкать на кнопке отправки данных. На этом этапе код может выполнить определенную работу с классами пространства имен System.IO. Во-первых, понадобится создать объект DirectoryInfo, который представляет каталог, выбранный пользователем. Во-вторых, с помощью метода DirectoryInfo.GetFiles необходимо получить набор объектов FileInfo, которые представляют файлы в этом каталоге. И, наконец, код должен привязать массив объектов FileInfo к GridView, как показано в следующем примере:
protected void cmdShow_Click(object sender, EventArgs e)
{
// Получить строковый массив со всеми файлами изображений
DirectoryInfo dir = new DirectoryInfo(txtDir.Text);
gridThumbs.DataSource = dir.GetFiles();
// Построить GridView
gridThumbs.DataBind();
}
Способ отображения связанных объектов FileInfo определяется шаблоном GridView. В этом примере требуется отобразить два элемента информации - краткое имя файла и соответствующий эскиз. Отображение краткого имени не представляет сложности. Достаточно выполнить привязку к свойству FileInfo.Name. Отображение эскиза требует использования дескриптора <img> для обращения к странице ThumbailsViewer.aspx. Однако построение правильного URL-адреса может оказаться не такой простой задачей, поэтому лучшее решение состоит в том, чтобы перепоручить эту работу методу GetImageUrl из класса веб-страницы.
Полное объявление GridView с применением шаблона имеет следующий вид:
<form id="form1" runat="server">
<div>
<asp:Label ID="Label1" runat="server" Text="Папка:"></asp:Label>
<asp:TextBox ID="txtDir" runat="server"></asp:TextBox>
<br /><br />
<asp:Button ID="cmdShow" runat="server" OnClick="cmdShow_Click"
Text="Показать эскизы" />
<br />
<asp:GridView ID="gridThumbs" runat="server" AutoGenerateColumns="False" Font-Names="Verdana"
Font-Size="X-Small" GridLines="None">
<Columns>
<asp:TemplateField>
<ItemTemplate>
<img src='<%# GetImageUrl(Eval("FullName")) %>' />
<%# Eval("Name") %>
<hr />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
</div>
</form>
Метод GetImageUrl() исследует полный путь к файлу, кодирует его и добавляет код в строку запроса, что позволяет ThumbailsViewer.aspx найти необходимый файл. Одновременно метод GetImageUrl() выбирает размер эскиза равный 50x50 пикселей. Обратите внимание, что путь к файлу является URL-закодированным в соответствии. Это связано с тем, что обычно имена файлов включают символы, которые не допускаются в URL-адресах, вроде пробелов:
protected string GetImageUrl(object path)
{
return "ThumbailsViewer.aspx?x=50&y=50&FilePath=" +
Server.UrlEncode((string)path);
}