Поделиться через


Отображение двоичных данных в веб-элементах управления данными (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. Таким образом, вы можете идти вперед и добавить файлы брошюры в файловую систему веб-сервера. Однако это не обновляет BrochurePath столбец в Categories таблице. В следующем руководстве мы посмотрим, как это сделать, но теперь нам нужно вручную указать значения для этого столбца.

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

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

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

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

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

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

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

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

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

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

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

После завершения работы мастера настройки источника данных Visual Studio автоматически добавит BoundField в Categories GridView для CategoryID, CategoryNameи BrochurePath DescriptionNumberOfProductsDataColumn s. Запустите и удалите NumberOfProducts BoundField, так как GetCategories() запрос метода не извлекает эти сведения. Также удалите BoundField и переименуйте CategoryID BrochurePath CategoryName свойства BoundFields HeaderText в категории и брошюры соответственно. После внесения этих изменений декларативная разметка 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. Морепродукты, которые имеют значение для негоBrochurePath, отображает пустую NULL ячейку.

Имя, описание и значение Брошюры для каждой категории перечислены

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Будет создан шаблонField с ItemTemplate элементом управления HyperLink Web, свойство которого 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. При этом оставьте флажок "Выбор главной страницы" снят. Эта страница ожидает 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 метода класса 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[], это может быть вызвано двумя вещами. Во-первыхPicture, Categories столбец таблицы разрешает 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 оператор метода s GetCategoryWithBinaryDataByCategoryID SELECT вернулся к списку столбцов основного запроса, что может произойти, если вы используете нерегламентированные инструкции SQL, и вы повторно запустите мастер для основного запроса TableAdapter. Убедитесь, что GetCategoryWithBinaryDataByCategoryID оператор метода SELECT по-прежнему Picture содержит столбец.

Примечание.

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

Однако для реализации этого поведения требуется добавить PictureLastModified столбец в Categories таблицу для отслеживания последнего Picture обновления столбца, а также кода для проверки заголовка If-Modified-Since . Дополнительные сведения о заголовке и условном рабочем процессе GET см. в If-Modified-Since статье HTTP Условный GET для хакеров RSS и более подробный обзор выполнения HTTP-запросов на странице ASP.NET.

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

Теперь, когда у нас есть веб-страница для отображения изображения определенной категории, мы можем отобразить его с помощью элемента управления "Веб-изображение" или элемента HTML <img> , указывающего на DisplayCategoryPicture.aspx?CategoryID=categoryIDнего. Изображения, URL-адрес которых определяется данными базы данных, можно отобразить в GridView или DetailsView с помощью ImageField. ImageField содержит DataImageUrlField и свойства, которые работают так же, как и свойства HyperLinkField DataNavigateUrlFields DataNavigateUrlFormatString.DataImageUrlFormatString

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

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

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

Об авторе

Скотт Митчелл, автор семи книг ASP/ASP.NET и основатель 4GuysFromRolla.com, работает с технологиями Microsoft Web с 1998 года. Скотт работает независимым консультантом, тренером и писателем. Его последняя книга Сэмс Учит себя ASP.NET 2.0 в 24 часах. Он может быть достигнут в mitchell@4GuysFromRolla.com. или через его блог, который можно найти на http://ScottOnWriting.NET.

Особое спасибо

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