Отображение двоичных данных в веб-элементах управления данными (C#)

Скотт Митчелл

Загрузить PDF-файл

В этом руководстве мы рассмотрим параметры представления двоичных данных на веб-странице, включая отображение файла изображения и подготовку ссылки "Скачать" для PDF-файла.

Введение

В предыдущем руководстве мы изучили два метода связывания двоичных данных с базовой моделью данных приложения и использовали элемент управления FileUpload для отправки файлов из браузера в файловую систему веб-сервера. Мы еще не узнали, как связать отправленные двоичные данные с моделью данных. То есть после отправки файла и сохранения в файловой системе путь к файлу должен храниться в соответствующей записи базы данных. Если данные хранятся непосредственно в базе данных, то отправленные двоичные данные не нужно сохранять в файловой системе, а внедрять в базу данных.

Однако прежде чем мы рассмотрим связывание данных с моделью данных, давайте сначала рассмотрим, как предоставить двоичные данные конечному пользователю. Представление текстовых данных достаточно простое, но как следует представлять двоичные данные? Это зависит, конечно, от типа двоичных данных. Для изображений, скорее всего, нужно отобразить изображение; Для PDF-файлов, microsoft Word документы, ZIP-файлы и другие типы двоичных данных, скорее всего, будет удобнее предоставить ссылку для скачивания.

В этом руководстве мы рассмотрим, как представить двоичные данные вместе со связанными с ними текстовыми данными с помощью веб-элементов управления данными, таких как GridView и DetailsView. В следующем руководстве мы рассмотрим связывание отправленного файла с базой данных.

Шаг 1. Предоставление значенийBrochurePath

Столбец Picture в Categories таблице уже содержит двоичные данные для различных изображений категорий. В частности, Picture столбец для каждой записи содержит двоичное содержимое зернистого 16-цветного растрового изображения низкого качества. Каждое изображение категории имеет ширину 172 пикселя и высоту 120 пикселей и занимает около 11 КБ. Более того, двоичное содержимое в столбце Picture содержит 78-байтный заголовок OLE , который необходимо удалить перед отображением изображения. Эти сведения о заголовке присутствуют, так как база данных Northwind имеет свои корни в Microsoft Access. В Access двоичные данные хранятся с помощью типа данных OLE Object, который применяется к этому заголовку. Пока мы посмотрим, как удалить заголовки из этих изображений низкого качества, чтобы отобразить рисунок. В следующем руководстве мы создадим интерфейс для обновления столбца Picture категории и заменим эти растровые изображения, использующие заголовки OLE, эквивалентными изображениями JPG без ненужных заголовков OLE.

В предыдущем руководстве мы узнали, как использовать элемент управления FileUpload. Таким образом, вы можете добавить файлы буклетов в файловую систему веб-сервера. Однако при этом столбец в таблице не обновляется BrochurePathCategories . В следующем руководстве мы посмотрим, как это сделать, но пока нам нужно вручную указать значения для этого столбца.

В этом руководстве вы найдете в ~/Brochures папке семь pdf-файлов буклетов, по одному для каждой категории, кроме морепродуктов. Я намеренно опущен добавление брошюры "Морепродукты", чтобы проиллюстрировать, как обрабатывать сценарии, когда не все записи имеют связанные двоичные данные. Чтобы обновить таблицу Categories с этими значениями, щелкните правой кнопкой мыши Categories узел в Обозреватель сервера и выберите пункт Показать данные таблицы. Затем введите виртуальные пути к файлам буклетов для каждой категории, в которых есть брошюра, как показано на рисунке 1. Так как для категории Морепродукты нет брошюры, оставьте BrochurePath значение столбца как NULL.

Вручную введите значения для столбца Categories Tables BrochurePath

Рис. 1. Введите значения столбца CategoriesBrochurePath таблицы вручную (щелкните для просмотра полноразмерного изображения)

С помощью значений BrochurePath , предоставленных Categories для таблицы, мы вновь готовы создать Элемент GridView, который перечисляет каждую категорию, а также ссылку для скачивания брошюры категории. На шаге 4 мы расширим этот gridView, чтобы отобразить изображение категории.

Начните с перетаскивания GridView с панели элементов на Designer DisplayOrDownloadData.aspx страницы в папкеBinaryData. Задайте для gridView значение IDCategories и с помощью смарт-тега GridView выберите привязку к новому источнику данных. В частности, привяжите его к объекту ObjectDataSource с именем CategoriesDataSource , который извлекает данные с помощью CategoriesBLL метода объекта GetCategories() .

Создание объекта ObjectDataSource с именем CategoriesDataSource

Рис. 2. Создание объекта ObjectDataSource с именем CategoriesDataSource (щелкните, чтобы просмотреть полноразмерное изображение)

Настройка ObjectDataSource для использования класса CategoriesBLL

Рис. 3. Настройка ObjectDataSource для использования CategoriesBLL класса (щелкните для просмотра полноразмерного изображения)

Получение списка категорий с помощью метода GetCategories()

Рис. 4. Получение списка категорий с помощью GetCategories() метода (щелкните для просмотра полноразмерного изображения)

После завершения работы мастера настройки источника данных Visual Studio автоматически добавит BoundField в Categories GridView для CategoryID, CategoryName, Description, NumberOfProductsи BrochurePathDataColumn . NumberOfProducts Удалите BoundField, так как GetCategories() запрос метода не получает эти сведения. Также удалите CategoryID BoundField и переименуйте CategoryName свойства и BrochurePath BoundFields HeaderText в Category и Буклет соответственно. После внесения этих изменений декларативная разметка GridView и ObjectDataSource должна выглядеть следующим образом:

<asp:GridView ID="Categories" runat="server" 
    AutoGenerateColumns="False" DataKeyNames="CategoryID"
    DataSourceID="CategoriesDataSource" EnableViewState="False">
    <Columns>
        <asp:BoundField DataField="CategoryName" HeaderText="Category" 
            SortExpression="CategoryName" />
        <asp:BoundField DataField="Description" HeaderText="Description" 
            SortExpression="Description" />
        <asp:BoundField DataField="BrochurePath" HeaderText="Brochure" 
            SortExpression="BrochurePath" />
    </Columns>
</asp:GridView>
<asp:ObjectDataSource ID="CategoriesDataSource" runat="server" 
    OldValuesParameterFormatString="original_{0}"
    SelectMethod="GetCategories" TypeName="CategoriesBLL">
</asp:ObjectDataSource>

Просмотрите эту страницу в браузере (см. рис. 5). Указана каждая из восьми категорий. Семь категорий со значениями BrochurePath имеют значение, BrochurePath отображаемое в соответствующем BoundField. Морепродукты, которые имеют NULL значение для , BrochurePathотображает пустую ячейку.

Для каждой категории указано имя, описание и значение путь к буклету

Рис. 5. Имя, описание и BrochurePath значение каждой категории отображаются в списке (щелкните, чтобы просмотреть полноразмерное изображение)

Вместо отображения текста столбца BrochurePath мы хотим создать ссылку на брошюру. Для этого удалите BrochurePath BoundField и замените его на HyperLinkField. Задайте для нового свойства HyperLinkField HeaderText значение Буклет, для его Text свойства — значение Просмотреть брошюру, а для свойства DataNavigateUrlFields — значение BrochurePath.

Добавление HyperLinkField для BrochurePath

Рис. 6. Добавление HyperLinkField для BrochurePath

При этом будет добавлен столбец ссылок на GridView, как показано на рисунке 7. Если щелкнуть ссылку Просмотреть брошюру, pdf-файл отобразится непосредственно в браузере или пользователю будет предложено скачать файл в зависимости от того, установлено ли средство чтения PDF и параметры браузера.

Брошюру категории можно просмотреть, щелкнув ссылку

Рис. 7. Брошюру категории можно просмотреть, щелкнув ссылку "Просмотреть брошюру" (щелкните для просмотра полноразмерного изображения)

Отображается PDF-файл брошюры категории

Рис. 8. Отображается PDF-файл брошюры категории (щелкните для просмотра полноразмерного изображения)

Скрытие текста брошюры для категорий без брошюры

Как показано на рисунке BrochurePath 7, HyperLinkField отображает значение свойства Text ("Просмотреть брошюру") для всех записей, независимо от того, имеется ли значение, отличноеNULL от значения для BrochurePath. Конечно, если BrochurePath имеет значение NULL, ссылка отображается только в виде текста, как в случае с категорией Морепродукты (см. рис. 7). Вместо того чтобы отображать текст Просмотреть брошюру, было бы неплохо, чтобы в этих категориях без BrochurePath значения отображались альтернативные тексты, например Нет брошюры.

Чтобы обеспечить такое поведение, необходимо использовать TemplateField, содержимое которого создается путем вызова метода страницы, который выдает соответствующие выходные данные на BrochurePath основе значения. Мы впервые изучили этот метод форматирования еще в руководстве Использование templateFields в элементе управления GridView .

Преобразуйте HyperLinkField в TemplateField, выбрав BrochurePath HyperLinkField и щелкнув ссылку Преобразовать это поле в TemplateField в диалоговом окне Изменение столбцов.

Преобразование HyperLinkField в TemplateField

Рис. 9. Преобразование HyperLinkField в TemplateField

При этом будет создан шаблон TemplateField с элементом управления ItemTemplate , содержащим веб-элемент управления HyperLink, свойство которого NavigateUrl привязано к значению BrochurePath . Замените эту разметку вызовом метода GenerateBrochureLink, передав значение BrochurePath:

<asp:TemplateField HeaderText="Brochure">
    <ItemTemplate>
        <%# GenerateBrochureLink(Eval("BrochurePath")) %>
    </ItemTemplate>
</asp:TemplateField>

Затем создайте protected метод в классе кода программной части ASP.NET страницы с именем GenerateBrochureLink , который возвращает string и принимает в object качестве входного параметра.

protected string GenerateBrochureLink(object BrochurePath)
{
    if (Convert.IsDBNull(BrochurePath))
        return "No Brochure Available";
    else
        return string.Format(@"<a href="{0}">View Brochure</a>", 
            ResolveUrl(BrochurePath.ToString()));
}

Этот метод определяет, является ли переданное object значение базой данных NULL , и, если да, возвращает сообщение о том, что в категории отсутствует брошюра. BrochurePath В противном случае значение отображается в гиперссылке. Обратите внимание, что если BrochurePath значение присутствует, оно передается в ResolveUrl(url) метод . Этот метод разрешает переданный URL-адрес, заменяя ~ символ соответствующим виртуальным путем. Например, если приложение имеет корневой каталог /Tutorial55, ResolveUrl("~/Brochures/Meats.pdf") возвращает /Tutorial55/Brochures/Meat.pdf.

На рисунке 10 показана страница после применения этих изменений. Обратите внимание, что в поле Категории морепродуктов BrochurePath теперь отображается текст Брошюра недоступна.

Текст Брошюра недоступна отображается для этих категорий без брошюры

Рис. 10. Текст брошюры недоступен для этих категорий без брошюры (щелкните для просмотра полноразмерного изображения)

Шаг 3. Добавление веб-страницы для отображения рисунка категории

Когда пользователь посещает страницу ASP.NET, он получает HTML-код страницы ASP.NET. Полученный HTML-код — это просто текст и не содержит двоичных данных. Любые дополнительные двоичные данные, такие как изображения, звуковые файлы, приложения Macromedia Flash, внедренные Медиаплеер Windows видео и т. д., существуют в виде отдельных ресурсов на веб-сервере. HTML-код содержит ссылки на эти файлы, но не содержит фактическое содержимое файлов.

Например, в HTML <img> элемент используется для ссылки на рисунок, при этом src атрибут указывает на файл изображения следующим образом:

<img src="MyPicture.jpg" ... />

Когда браузер получает этот HTML-код, он выполняет еще один запрос к веб-серверу, чтобы получить двоичное содержимое файла изображения, которое затем отображается в браузере. Та же концепция применяется к любым двоичным данным. На шаге 2 брошюра не была отправлена в браузер как часть HTML-разметки страницы. Вместо этого отображаемый HTML-код предоставлял гиперссылки, которые при щелчке заставили браузер запрашивать PDF-документ напрямую.

Чтобы отобразить или разрешить пользователям скачивать двоичные данные, хранящееся в базе данных, необходимо создать отдельную веб-страницу, которая возвращает эти данные. Для нашего приложения существует только одно поле двоичных данных, хранящееся непосредственно в базе данных изображения категории. Поэтому нам нужна страница, которая при вызове возвращает данные изображения для определенной категории.

Добавьте новую страницу ASP.NET в папку BinaryData с именем DisplayCategoryPicture.aspx. При этом не устанавливайте флажок Выбрать страницу master. Эта страница ожидает значение в строках CategoryID запроса и возвращает двоичные данные столбца Picture этой категории. Так как эта страница возвращает двоичные данные и ничего другого, ей не требуется разметка в разделе HTML. Поэтому щелкните вкладку Источник в левом нижнем углу и удалите всю разметку страницы, за исключением директивы <%@ Page %> . То есть DisplayCategoryPicture.aspx декларативная разметка должна состоять из одной строки:

<%@ Page Language="C#" AutoEventWireup="true" 
    CodeFile="DisplayCategoryPicture.aspx.cs" 
    Inherits="BinaryData_DisplayCategoryPicture" %>

Если вы видите MasterPageFile атрибут в директиве <%@ Page %> , удалите его.

В классе кода программной части страницы добавьте следующий код в Page_Load обработчик событий:

protected void Page_Load(object sender, EventArgs e)
{
    int categoryID = Convert.ToInt32(Request.QueryString["CategoryID"]);
    // Get information about the specified category
    CategoriesBLL categoryAPI = new CategoriesBLL();
    Northwind.CategoriesDataTable categories = 
        categoryAPI.GetCategoryWithBinaryDataByCategoryID(categoryID);
    Northwind.CategoriesRow category = categories[0];
    // Output HTTP headers providing information about the binary data
    Response.ContentType = "image/bmp";
    // Output the binary data
    // But first we need to strip out the OLE header
    const int OleHeaderLength = 78;
    int strippedImageLength = category.Picture.Length - OleHeaderLength;
    byte[] strippedImageData = new byte[strippedImageLength];
    Array.Copy(category.Picture, OleHeaderLength, 
        strippedImageData, 0, strippedImageLength);
    
    Response.BinaryWrite(strippedImageData);
}

Этот код начинается с чтения значения CategoryID строки запроса в переменную с именем categoryID. Затем данные рисунка извлекаются с помощью вызова CategoriesBLL метода класса s GetCategoryWithBinaryDataByCategoryID(categoryID) . Эти данные возвращаются клиенту с помощью Response.BinaryWrite(data) метода , но перед этим вызовом Picture необходимо удалить заголовок OLE столбца. Это достигается путем создания массива byte с именем strippedImageData , который будет содержать ровно 78 символов меньше, чем в столбце Picture . МетодArray.Copy используется для копирования данных из category.Picture позиции 78 strippedImageDataв .

Свойство Response.ContentType указывает тип MIME возвращаемого содержимого, чтобы браузер знал, как его отобразить. Categories Так как столбец таблицы Picture является растровым изображением, здесь используется тип MIME растрового изображения (image/bmp). Если опустить тип MIME, большинство браузеров по-прежнему будут отображать изображение правильно, так как они могут определить тип на основе содержимого двоичных данных файла изображения. Однако рекомендуется включать тип MIME, когда это возможно. Полный список типов мультимедиа MIME см. на веб-сайте Центра по назначенным в Интернете номерам.

После создания этой страницы можно просмотреть изображение определенной категории, посетив DisplayCategoryPicture.aspx?CategoryID=categoryIDстраницу . На рисунке 11 показано изображение категории "Напитки", которое можно просмотреть на DisplayCategoryPicture.aspx?CategoryID=1.

Отображается изображение категории

Рис. 11. Отображается изображение категории напитков (щелкните для просмотра полноразмерного изображения)

Если при посещении DisplayCategoryPicture.aspx?CategoryID=categoryIDпоявляется исключение с сообщением Не удалось привести объект типа System.DBNull к типу System.Byte[], это может быть вызвано двумя способами. Во-первых, столбец Categories таблицы Picture допускает NULL значения. Однако DisplayCategoryPicture.aspx на странице предполагается, что присутствует значение, отличноеNULL от значения. Невозможно Picture получить прямой доступ к свойству CategoriesDataTable объекта , если оно имеет NULL значение . Если вы хотите разрешить NULL значения для столбца Picture , необходимо включить следующее условие:

if (category.IsPictureNull())
{
    // Display some "No Image Available" picture
    Response.Redirect("~/Images/NoPictureAvailable.gif");
}
else
{
    // Send back the binary contents of the Picture column
    // ... Set ContentType property and write out ...
    // ... data via Response.BinaryWrite ...
}

В приведенном выше коде предполагается, что в Images папке есть файл NoPictureAvailable.gif изображения, который будет отображаться для этих категорий без рисунка.

Это исключение также может быть вызвано тем, что CategoriesTableAdapter оператор метода SELECT s GetCategoryWithBinaryDataByCategoryID вернулся к списку столбцов запроса main, что может произойти при использовании нерегламентированных инструкций SQL и повторном запуске мастера для запроса main TableAdapter. Убедитесь, что GetCategoryWithBinaryDataByCategoryID оператор метода SELECT по-прежнему Picture содержит столбец.

Примечание

При каждом посещении DisplayCategoryPicture.aspx выполняется доступ к базе данных и возвращаются данные рисунка указанной категории. Однако если изображение категории не изменилось с момента последнего просмотра пользователем, это напрасно. К счастью, HTTP позволяет использовать условные методы GET. При использовании условного GET клиент, выполняющий HTTP-запрос, отправляет заголовокIf-Modified-Since HTTP, который предоставляет дату и время последнего получения клиентом этого ресурса с веб-сервера. Если содержимое не изменилось с указанной даты, веб-сервер может ответить кодом состояния "Не изменено" (304) и отказаться от отправки обратного содержимого запрошенного ресурса. Короче говоря, этот метод избавляет веб-сервер от необходимости отправлять обратно содержимое для ресурса, если оно не было изменено с момента последнего доступа клиента к нему.

Однако для реализации этого поведения необходимо добавить PictureLastModified столбец в Categories таблицу для записи времени Picture последнего обновления столбца, а также код для проверка заголовкаIf-Modified-Since. Дополнительные сведения о заголовке If-Modified-Since и условном рабочем процессе GET см. в разделах Http Conditional GET for RSS Hackers и A Deeper Look at Performing HTTP Requests in a ASP.NET Page.

Шаг 4. Отображение изображений категорий в GridView

Теперь, когда у нас есть веб-страница для отображения изображения определенной категории, мы можем отобразить ее с помощью элемента управления Image Web или ЭЛЕМЕНТА HTML <img> , указывающего на DisplayCategoryPicture.aspx?CategoryID=categoryID. Изображения, URL-адрес которых определяется данными базы данных, можно отобразить в GridView или DetailsView с помощью ImageField. ImageField содержит DataImageUrlField свойства и DataImageUrlFormatString , которые работают так же, как свойства и DataNavigateUrlFormatString HyperLinkFieldDataNavigateUrlFields.

Давайте добавим Categories GridView в DisplayOrDownloadData.aspx , добавив ImageField для отображения рисунка каждой категории. Просто добавьте ImageField и задайте для его DataImageUrlField свойств CategoryID и DataImageUrlFormatString значения и DisplayCategoryPicture.aspx?CategoryID={0}соответственно. При этом будет создан столбец GridView, который отображает <img> элемент, атрибут которого src ссылается на DisplayCategoryPicture.aspx?CategoryID={0}, где {0} заменяется значением строки CategoryID GridView.

Добавление ImageField в GridView

Рис. 12. Добавление ImageField в GridView

После добавления ImageField декларативный синтаксис GridView должен выглядеть следующим образом:

<asp:GridView ID="Categories" runat="server" AutoGenerateColumns="False" 
    DataKeyNames="CategoryID" DataSourceID="CategoriesDataSource" 
    EnableViewState="False">
    <Columns>
        <asp:BoundField DataField="CategoryName" HeaderText="Category" 
            SortExpression="CategoryName" />
        <asp:BoundField DataField="Description" HeaderText="Description" 
            SortExpression="Description" />
        <asp:TemplateField HeaderText="Brochure">
            <ItemTemplate>
                <%# GenerateBrochureLink(Eval("BrochurePath")) %>
            </ItemTemplate>
        </asp:TemplateField>
        <asp:ImageField DataImageUrlField="CategoryID" 
            DataImageUrlFormatString="DisplayCategoryPicture.aspx?CategoryID={0}">
        </asp:ImageField>
    </Columns>
</asp:GridView>

Просмотрите эту страницу в браузере. Обратите внимание, что каждая запись теперь включает в себя изображение для категории.

Изображение категории отображается для каждой строки

Рис. 13. Изображение категории отображается для каждой строки (щелкните для просмотра полноразмерного изображения)

Сводка

В этом руководстве мы рассмотрели, как представить двоичные данные. Способ представления данных зависит от типа данных. Для pdf-файлов брошюры мы предложили пользователю ссылку Просмотреть брошюру, которая при нажатии на нее перенапрянула пользователя непосредственно в PDF-файл. Для изображения категории мы сначала создали страницу для получения и возврата двоичных данных из базы данных, а затем использовали эту страницу для отображения рисунка каждой категории в GridView.

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

Счастливого программирования!

Об авторе

Скотт Митчелл( Scott Mitchell), автор семи книг ASP/ASP.NET и основатель 4GuysFromRolla.com, работает с веб-технологиями Майкрософт с 1998 года. Скотт работает независимым консультантом, тренером и писателем. Его последняя книга Sams Teach Yourself ASP.NET 2.0 в 24 часах. Он может быть доступен в mitchell@4GuysFromRolla.com. или через его блог, который можно найти по адресу http://ScottOnWriting.NET.

Особая благодарность

Эта серия учебников была рассмотрена многими полезными рецензентами. Ведущим рецензентом этого руководства были Тетера Мерфи и Дэйв Гарднер. Хотите просмотреть предстоящие статьи MSDN? Если да, опустите мне строку на mitchell@4GuysFromRolla.com.