Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
В этом руководстве показано, как создать веб-интерфейс, позволяющий пользователю вводить текстовые данные и отправлять двоичные файлы. Чтобы проиллюстрировать параметры, доступные для хранения двоичных данных, один файл будет сохранен в базе данных, а другой хранится в файловой системе.
Введение
В предыдущих двух руководствах мы изучили методы хранения двоичных данных, связанных с моделью данных приложения, рассмотрели, как использовать элемент управления FileUpload для отправки файлов с клиента на веб-сервер и узнали, как представить эти двоичные данные в веб-элементе управления данными. Мы еще не поговорим о том, как связать отправленные данные с моделью данных.
В этом руководстве мы создадим веб-страницу для добавления новой категории. Помимо TextBoxes для имени и описания категории, на этой странице потребуется включить два элемента управления FileUpload для рисунка новой категории и одну для брошюры. Загруженный рисунок будет храниться непосредственно в столбце новой записи Picture
, в то время как брошюра будет сохранена ~/Brochures
в папке с путем к файлу, сохраненном в столбце новой записи BrochurePath
.
Перед созданием этой веб-страницы необходимо обновить архитектуру. Основной запрос CategoriesTableAdapter
не извлекает столбец Picture
. Следовательно, автоматически созданный Insert
метод имеет только входные данные для CategoryName
Description
полей и BrochurePath
полей. Поэтому необходимо создать дополнительный метод в TableAdapter, который запрашивает все четыре Categories
поля. Класс CategoriesBLL
в уровне бизнес-логики также потребуется обновить.
Шаг 1. ДобавлениеInsertWithPicture
метода вCategoriesTableAdapter
Когда мы создали CategoriesTableAdapter
в руководстве по созданию уровня доступа к данным, мы настроили его для автоматического создания инструкций INSERT
, UPDATE
, и DELETE
на основе основного запроса. Кроме того, мы поручили TableAdapter использовать прямой подход базы данных, который создал методы Insert
, Update
и Delete
. Эти методы выполняют автоматически созданные операторы INSERT
, UPDATE
, и DELETE
и, следовательно, принимают входные параметры на основе столбцов, возвращаемых основным запросом. В руководстве по загрузке файлов мы дополнили CategoriesTableAdapter
основной запрос, чтобы использовать столбец BrochurePath
.
CategoriesTableAdapter
Так как основной запрос не ссылается на Picture
столбец, мы не можем добавлять новую запись или обновлять существующую запись со значением для столбцаPicture
. Чтобы получить эти сведения, можно создать новый метод в TableAdapter, который используется специально для вставки записи с двоичными данными или настроить автоматически созданную INSERT
инструкцию. Проблема с настройкой автоматически созданной INSERT
инструкции заключается в том, что существует риск того, что наши настройки будут перезаписаны мастером. Например, представьте, что мы настроили инструкцию INSERT
так, чтобы она включала использование столбца Picture
. Это обновит метод TableAdapter Insert
, чтобы добавить дополнительный параметр ввода для бинарных данных изображения категории. Затем мы могли бы создать метод на уровне бизнес-логики, чтобы использовать этот метод DAL и вызвать этот метод BLL через уровень презентации, и все будет работать замечательно. То есть до следующего момента, когда мы настроили TableAdapter с помощью мастера настройки TableAdapter. Как только мастер завершил работу, наши настройки инструкции INSERT
будут перезаписаны, Insert
метод вернется к старой форме, и наш код больше не компилируется!
Замечание
Это раздражение не является проблемой при использовании хранимых процедур вместо нерегламентированных инструкций SQL. В будущем руководстве показано использование хранимых процедур вместо нерегламентированных инструкций SQL на уровне доступа к данным.
Чтобы избежать этой потенциальной головной боли, а не настраивать автоматически созданные инструкции SQL, давайте создадим новый метод для TableAdapter. Этот метод, именованный InsertWithPicture
, принимает значения для столбцов CategoryName
, Description
, BrochurePath
и Picture
и выполняет оператор INSERT
, который сохраняет все четыре значения в новой записи.
Откройте типизированный набор данных и в конструкторе щелкните правой кнопкой мыши на заголовке CategoriesTableAdapter
и выберите "Добавить запрос" в контекстном меню. Откроется мастер конфигурации запросов TableAdapter, который начнет с вопроса, каким образом запрос TableAdapter должен получить доступ к базе данных. Нажмите кнопку "Использовать инструкции SQL" и нажмите кнопку "Далее". Следующий шаг запрашивает тип создаваемого запроса. Так как мы создадим запрос для добавления новой записи в Categories
таблицу, нажмите кнопку INSERT и нажмите кнопку "Далее".
Рис. 1. Выберите параметр INSERT (щелкните, чтобы просмотреть изображение полного размера)
Теперь необходимо указать инструкцию INSERT
SQL. Мастер автоматически предлагает INSERT
инструкцию, соответствующую основному запросу TableAdapter-а. В этом случае это INSERT
инструкция, которая вставляет значения CategoryName
, Description
и BrochurePath
. Обновите инструкцию, чтобы Picture
столбец был включен вместе с параметром @Picture
, как показано ниже.
INSERT INTO [Categories]
([CategoryName], [Description], [BrochurePath], [Picture])
VALUES
(@CategoryName, @Description, @BrochurePath, @Picture)
Последний экран мастера предлагает нам указать название для нового метода TableAdapter. Введите InsertWithPicture
и нажмите кнопку "Готово".
Рис. 2. Назовите метод InsertWithPicture
New TableAdapter (щелкните, чтобы просмотреть изображение полного размера)
Шаг 2. Обновление уровня бизнес-логики
Так как уровень презентации должен работать только с уровнем бизнес-логики, а не обойти его, чтобы перейти непосредственно к уровню доступа к данным, необходимо создать метод BLL, который вызывает только что созданный метод DAL (InsertWithPicture
). В этом руководстве создайте метод в классе CategoriesBLL
, который принимает на вход три InsertWithPicture
и массив string
. Входные string
параметры предназначены для имени категории, описания и пути к файлу брошюры, а byte
массив предназначен для двоичного содержимого рисунка категории. Как показано в следующем коде, этот метод BLL вызывает соответствующий метод DAL:
[System.ComponentModel.DataObjectMethodAttribute
(System.ComponentModel.DataObjectMethodType.Insert, false)]
public void InsertWithPicture(string categoryName, string description,
string brochurePath, byte[] picture)
{
Adapter.InsertWithPicture(categoryName, description, brochurePath, picture);
}
Замечание
Перед добавлением InsertWithPicture
метода в BLL необходимо сохранить типизированный набор данных. Поскольку код класса CategoriesTableAdapter
создается автоматически на основе типизированного DataSet, если вы не сначала сохраните изменения в типизированном наборе данных, свойство Adapter
не будет знать о методе InsertWithPicture
.
Шаг 3. Перечисление существующих категорий и их двоичных данных
В этом руководстве мы создадим страницу, которая позволяет пользователю добавлять новую категорию в систему, предоставляя рисунок и брошюру для новой категории. В предыдущем руководстве мы использовали GridView с шаблонным полем и ImageField, чтобы отобразить имя каждой категории, описание, рисунок и ссылку, чтобы скачать ее брошюру. Давайте реплицируем эту функцию для этого руководства, создав страницу, которая отображает все существующие категории и позволяет создавать новые.
Начните с открытия DisplayOrDownload.aspx
страницы из BinaryData
папки. Перейдите в представление Source и скопируйте декларативный синтаксис GridView и ObjectDataSource, вставив его внутрь элемента <asp:Content>
в UploadInDetailsView.aspx
. Кроме того, не забудьте скопировать метод GenerateBrochureLink
из класса DisplayOrDownload.aspx
code-behind в UploadInDetailsView.aspx
.
Рис. 3. Копирование и вставка декларативного синтаксиса из DisplayOrDownload.aspx
(UploadInDetailsView.aspx
щелкните, чтобы просмотреть изображение полного размера)
После копирования декларативного синтаксиса и GenerateBrochureLink
метода UploadInDetailsView.aspx
на страницу просмотрите страницу через браузер, чтобы убедиться, что все скопировано правильно. Вы увидите в GridView список восьми категорий, которые включают ссылку на скачивание брошюры, а также рисунок категории.
Рис. 4. Теперь вы увидите каждую категорию вместе с двоичными данными (щелкните, чтобы просмотреть изображение полного размера)
Шаг 4. Настройка конфигурации CategoriesDataSource
для поддержки вставки
CategoriesDataSource
ObjectDataSource, используемый в Categories
GridView, в настоящее время не предоставляет возможность вставки данных. Чтобы обеспечить поддержку вставки с помощью этого элемента управления источниками данных, необходимо сопоставить его Insert
метод с методом в базовом объекте CategoriesBLL
. В частности, мы хотим сопоставить его с методомCategoriesBLL
, добавленным на шаге 2. InsertWithPicture
Начните, щелкнув ссылку "Настроить источник данных" из смарт-тега ObjectDataSource. На первом экране показан объект, с которым настроен источник данных для работы. CategoriesBLL
Оставьте этот параметр as-is и нажмите кнопку "Далее", чтобы перейти к экрану "Определение методов данных". Перейдите на вкладку INSERT и выберите InsertWithPicture
метод из раскрывающегося списка. Нажмите кнопку "Готово", чтобы завершить работу мастера.
Рис. 5. Настройка ObjectDataSource для использования InsertWithPicture
метода (щелкните, чтобы просмотреть изображение полного размера)
Замечание
После завершения работы мастера Visual Studio может попросить обновить поля и ключи, которые будут повторно создавать поля веб-элементов управления данными. Выберите "Нет", так как выбор "Да" перезаписывает любые настройки полей, которые вы могли сделать.
После завершения работы мастера ObjectDataSource теперь будет содержать значение для свойства InsertMethod
, а также InsertParameters
для четырех столбцов категорий, как показано в следующей декларативной разметке.
<asp:ObjectDataSource ID="CategoriesDataSource" runat="server"
OldValuesParameterFormatString="original_{0}" SelectMethod="GetCategories"
TypeName="CategoriesBLL" InsertMethod="InsertWithPicture">
<InsertParameters>
<asp:Parameter Name="categoryName" Type="String" />
<asp:Parameter Name="description" Type="String" />
<asp:Parameter Name="brochurePath" Type="String" />
<asp:Parameter Name="picture" Type="Object" />
</InsertParameters>
</asp:ObjectDataSource>
Шаг 5. Создание интерфейса вставки
Как описано в разделе "Обзор вставки, обновления и удаления данных", элемент управления DetailsView предоставляет встроенный интерфейс вставки, который можно использовать при работе с элементом управления источником данных, поддерживающим вставку. Давайте добавим элемент управления DetailsView на эту страницу над GridView, который будет постоянно отображать его интерфейс вставки, что позволяет пользователю быстро добавлять новую категорию. После добавления новой категории в DetailsView, расположенный под ним GridView автоматически обновится и отобразит новую категорию.
Начните с перетаскивания DetailsView из панели элементов в конструктор над GridView, задав его ID
и NewCategory
свойства, и очистите Height
и Width
значения свойств. Из смарт-тега DetailsView привяжите к существующему CategoriesDataSource
, а затем установите флажок "Разрешить вставку".
Рис. 6. Привязка DetailsView к CategoriesDataSource
и включение вставки (щелкните, чтобы просмотреть изображение полного размера)
Чтобы постоянно отобразить DetailsView в режиме вставки, задайте для его свойства DefaultMode
значение Insert
.
Обратите внимание, что DetailsView имеет пять BoundFields CategoryID
, CategoryName
, Description
, NumberOfProducts
, и BrochurePath
, хотя CategoryID
BoundField не отображается в интерфейсе вставки, так как его InsertVisible
свойство имеет значение false
. Эти BoundFields существуют, поскольку они являются столбцами, возвращаемыми методом GetCategories()
, который объект ObjectDataSource вызывает для получения данных. Однако для вставки мы не хотим разрешить пользователю указать значение для NumberOfProducts
. Кроме того, нам нужно разрешить им отправить фотографию для новой категории, а также отправить PDF-файл для брошюры.
NumberOfProducts
Удалите BoundField из DetailsView полностью, а затем обновите HeaderText
свойства CategoryName
и BrochurePath
BoundFields на категорию и брошюру соответственно. Затем преобразуйте BrochurePath
BoundField в TemplateField и добавьте новый TemplateField для рисунка, присвоив этому новому TemplateField значение HeaderText
Picture. Переместите Picture
TemplateField таким образом, чтобы он находился между BrochurePath
TemplateField и CommandField.
Рис. 7. Привязка DetailsView к CategoriesDataSource
и включение вставки
Если вы преобразовали BrochurePath
BoundField в TemplateField с помощью диалогового окна "Изменить поля", TemplateField включает ItemTemplate
, EditItemTemplate
и InsertItemTemplate
. Требуется только InsertItemTemplate
, поэтому вы можете удалить остальные два шаблона. На этом этапе декларативный синтаксис DetailsView должен выглядеть следующим образом:
<asp:DetailsView ID="NewCategory" runat="server" AutoGenerateRows="False"
DataKeyNames="CategoryID" DataSourceID="CategoriesDataSource"
DefaultMode="Insert">
<Fields>
<asp:BoundField DataField="CategoryID" HeaderText="CategoryID"
InsertVisible="False" ReadOnly="True"
SortExpression="CategoryID" />
<asp:BoundField DataField="CategoryName" HeaderText="Category"
SortExpression="CategoryName" />
<asp:BoundField DataField="Description" HeaderText="Description"
SortExpression="Description" />
<asp:TemplateField HeaderText="Brochure" SortExpression="BrochurePath">
<InsertItemTemplate>
<asp:TextBox ID="TextBox1" runat="server"
Text='<%# Bind("BrochurePath") %>'></asp:TextBox>
</InsertItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Picture"></asp:TemplateField>
<asp:CommandField ShowInsertButton="True" />
</Fields>
</asp:DetailsView>
Добавление элементов управления FileUpload для полей брошюры и рисунков
В настоящее время BrochurePath
TemplateFields содержит InsertItemTemplate
текстовое поле, а Picture
TemplateField не содержит шаблонов. Необходимо обновить эти два TemplateField InsertItemTemplate
, чтобы использовать элементы управления типа FileUpload.
В смарт-теге DetailsView выберите параметр "Изменить шаблоны", а затем выберите BrochurePath
templateField s InsertItemTemplate
из раскрывающегося списка. Удалите textBox и перетащите элемент управления FileUpload из панели элементов в шаблон. Задайте для элемента управления FileUpload значение ID
BrochureUpload
. Аналогичным образом добавьте элемент управления FileUpload в Picture
s InsertItemTemplate
TemplateField. Задайте этому элементу управления FileUpload значение из тегов ID
и PictureUpload
.
Рис. 8. Добавление элемента управления FileUpload в элемент InsertItemTemplate
(щелкните, чтобы увидеть изображение в полном размере)
После добавления этих дополнений будет использоваться два декларативных синтаксиса TemplateField:
<asp:TemplateField HeaderText="Brochure" SortExpression="BrochurePath">
<InsertItemTemplate>
<asp:FileUpload ID="BrochureUpload" runat="server" />
</InsertItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Picture">
<InsertItemTemplate>
<asp:FileUpload ID="PictureUpload" runat="server" />
</InsertItemTemplate>
</asp:TemplateField>
Когда пользователь добавляет новую категорию, мы хотим убедиться, что брошюра и рисунок имеют правильный тип файла. Для брошюры пользователь должен предоставить PDF-файл. Для рисунка пользователю требуется отправить файл изображения, но разрешать любой файл изображения или только файлы изображений определенного типа, например GIF или JPG? Чтобы разрешить различные типы файлов, необходимо расширить Categories
схему, чтобы включить столбец, который фиксирует тип файла, чтобы этот тип можно было отправить клиенту через Response.ContentType
DisplayCategoryPicture.aspx
. Так как у нас нет такого столбца, было бы разумно ограничить пользователей только определенным типом файла изображения. Существующие Categories
изображения таблицы являются растровыми, хотя для изображений, предназначенных для Интернета, более подходящим форматом файлов являются JPG.
Если пользователь отправляет неправильный тип файла, необходимо отменить вставку и отобразить сообщение, указывающее на проблему. Добавьте веб-элемент управления Label под DetailsView. Задайте для свойства ID
значение UploadWarning
, очистите его свойство Text
, задайте для свойства CssClass
значение Warning, а для свойств Visible
и EnableViewState
значение false
. Класс Warning
CSS определяется в Styles.css
и визуализирует текст в большом, красном, курсивном, полужирном шрифте.
Замечание
В идеале CategoryName
и Description
BoundFields будут преобразованы в TemplateFields с настроенными интерфейсами вставки. Интерфейс вставки Description
, например, лучше подходит для многострочного текстового поля. Поскольку столбец CategoryName
не принимает значения NULL
, необходимо добавить проверку обязательного поля, чтобы гарантировать, что пользователь указывает значение для имени новой категории. Эти действия остаются в качестве упражнения для читателя. Вернитесь к настройке интерфейса изменения данных для подробного изучения расширения интерфейсов изменения данных.
Шаг 6. Сохранение загруженной брошюры в файловой системе веб-сервера
Когда пользователь вводит значения новой категории и нажимает кнопку "Вставка", происходит обратная связь, и рабочий процесс вставки разворачивается. Во-первых, событие DetailsView ItemInserting
запускается. Затем вызывается метод ObjectDataSource Insert()
, который приводит к добавлению новой записи в таблицу Categories
. После этого событие DetailsView ItemInserted
запускается.
Перед вызовом метода ObjectDataSource Insert()
необходимо сначала убедиться, что соответствующие типы файлов были отправлены пользователем, а затем сохраните PDF-файл брошюры в файловой системе веб-сервера. Создайте обработчик событий для события DetailsView ItemInserting
и добавьте следующий код:
// Reference the FileUpload control
FileUpload BrochureUpload =
(FileUpload)NewCategory.FindControl("BrochureUpload");
if (BrochureUpload.HasFile)
{
// Make sure that a PDF has been uploaded
if (string.Compare(System.IO.Path.GetExtension
(BrochureUpload.FileName), ".pdf", true) != 0)
{
UploadWarning.Text =
"Only PDF documents may be used for a category's brochure.";
UploadWarning.Visible = true;
e.Cancel = true;
return;
}
}
Обработчик событий начинает с обращения к элементу управления BrochureUpload
FileUpload в шаблонах DetailsView. Затем, если брошюра была отправлена, проверяется расширение отправленного файла. Если расширение не .PDF, отображается предупреждение, вставка отменена, а выполнение обработчика событий заканчивается.
Замечание
Использование расширения загруженного файла не является надежным методом обеспечения того, что загруженный файл является PDF-документом. Пользователь может иметь действительный PDF-документ с расширением .Brochure
или мог бы принять документ, отличный от PDF, и дал ему .pdf
расширение. Чтобы более убедительно подтвердить тип файла, необходимо будет программно исследовать его двоичное содержимое. Такие тщательные подходы, однако, часто излишни; Проверка расширения является достаточной в большинстве случаев.
Как обсуждалось в руководстве по загрузке файлов, при сохранении файлов в файловой системе необходимо убедиться, что загрузка одного пользователя не перезаписывает файлы другого пользователя. В этом руководстве мы попытаемся использовать то же имя, что и отправленный файл. Если в каталоге уже существует файл ~/Brochures
с таким же именем файла, мы добавим число в конце до тех пор, пока не будет найдено уникальное имя. Например, если пользователь загружает файл брошюры с именем Meats.pdf
, но в папке Meats.pdf
уже существует файл с именем ~/Brochures
, мы изменим сохраненное имя файла на Meats-1.pdf
. Если это существует, мы попытаемся Meats-2.pdf
и т. д. до тех пор, пока не будет найдено уникальное имя файла.
Следующий код использует File.Exists(path)
метод , чтобы определить, существует ли файл с указанным именем файла. Если да, он продолжает пробовать новые названия файлов для брошюры до тех пор, пока конфликт не будет устранен.
const string BrochureDirectory = "~/Brochures/";
string brochurePath = BrochureDirectory + BrochureUpload.FileName;
string fileNameWithoutExtension =
System.IO.Path.GetFileNameWithoutExtension(BrochureUpload.FileName);
int iteration = 1;
while (System.IO.File.Exists(Server.MapPath(brochurePath)))
{
brochurePath = string.Concat(BrochureDirectory,
fileNameWithoutExtension, "-", iteration, ".pdf");
iteration++;
}
После обнаружения допустимого имени файла необходимо сохранить файл в файловой системе, а значение ObjectDataSource brochurePath``InsertParameter
необходимо обновить, чтобы это имя файла записывается в базу данных. Как мы видели в руководстве по отправке файлов , файл можно сохранить с помощью метода элемента управления SaveAs(path)
FileUpload. Чтобы обновить параметр ObjectDataSource brochurePath
, используйте коллекцию e.Values
.
// Save the file to disk and set the value of the brochurePath parameter
BrochureUpload.SaveAs(Server.MapPath(brochurePath));
e.Values["brochurePath"] = brochurePath;
Шаг 7. Сохранение отправленного рисунка в базу данных
Чтобы сохранить отправленное изображение в новой Categories
записи, необходимо назначить переданное двоичное содержимое параметру ObjectDataSource picture
в событии DetailsView ItemInserting
. Прежде чем сделать это задание, необходимо сначала убедиться, что загруженный рисунок — это JPG, а не другой тип изображения. Как и в шаге 6, давайте используем расширение файла отправленного рисунка для определения его типа.
Categories
В то время как таблица разрешает NULL
значения для столбцаPicture
, все категории в настоящее время имеют изображение. Давайте заставим пользователя предоставить рисунок при добавлении новой категории на этой странице. Следующий код проверяет, что изображение отправлено и имеет соответствующее расширение.
// Reference the FileUpload controls
FileUpload PictureUpload = (FileUpload)NewCategory.FindControl("PictureUpload");
if (PictureUpload.HasFile)
{
// Make sure that a JPG has been uploaded
if (string.Compare(System.IO.Path.GetExtension(PictureUpload.FileName),
".jpg", true) != 0 &&
string.Compare(System.IO.Path.GetExtension(PictureUpload.FileName),
".jpeg", true) != 0)
{
UploadWarning.Text =
"Only JPG documents may be used for a category's picture.";
UploadWarning.Visible = true;
e.Cancel = true;
return;
}
}
else
{
// No picture uploaded!
UploadWarning.Text =
"You must provide a picture for the new category.";
UploadWarning.Visible = true;
e.Cancel = true;
return;
}
Этот код следует разместить перед кодом из шага 6, чтобы, если возникнет проблема с загрузкой изображений, завершение работы обработчика событий произошло до сохранения файла брошюры в файловой системе.
При условии, что был отправлен соответствующий файл, присвойте загруженное двоичное содержимое параметру изображения, используя следующую строку кода:
// Set the value of the picture parameter
e.Values["picture"] = PictureUpload.FileBytes;
ПолныйItemInserting
обработчик событий
Для полноты ниже приведен ItemInserting
обработчик событий в полном объеме:
protected void NewCategory_ItemInserting(object sender, DetailsViewInsertEventArgs e)
{
// Reference the FileUpload controls
FileUpload PictureUpload = (FileUpload)NewCategory.FindControl("PictureUpload");
if (PictureUpload.HasFile)
{
// Make sure that a JPG has been uploaded
if (string.Compare(System.IO.Path.GetExtension(PictureUpload.FileName),
".jpg", true) != 0 &&
string.Compare(System.IO.Path.GetExtension(PictureUpload.FileName),
".jpeg", true) != 0)
{
UploadWarning.Text =
"Only JPG documents may be used for a category's picture.";
UploadWarning.Visible = true;
e.Cancel = true;
return;
}
}
else
{
// No picture uploaded!
UploadWarning.Text =
"You must provide a picture for the new category.";
UploadWarning.Visible = true;
e.Cancel = true;
return;
}
// Set the value of the picture parameter
e.Values["picture"] = PictureUpload.FileBytes;
// Reference the FileUpload controls
FileUpload BrochureUpload =
(FileUpload)NewCategory.FindControl("BrochureUpload");
if (BrochureUpload.HasFile)
{
// Make sure that a PDF has been uploaded
if (string.Compare(System.IO.Path.GetExtension(BrochureUpload.FileName),
".pdf", true) != 0)
{
UploadWarning.Text =
"Only PDF documents may be used for a category's brochure.";
UploadWarning.Visible = true;
e.Cancel = true;
return;
}
const string BrochureDirectory = "~/Brochures/";
string brochurePath = BrochureDirectory + BrochureUpload.FileName;
string fileNameWithoutExtension =
System.IO.Path.GetFileNameWithoutExtension(BrochureUpload.FileName);
int iteration = 1;
while (System.IO.File.Exists(Server.MapPath(brochurePath)))
{
brochurePath = string.Concat(BrochureDirectory, fileNameWithoutExtension,
"-", iteration, ".pdf");
iteration++;
}
// Save the file to disk and set the value of the brochurePath parameter
BrochureUpload.SaveAs(Server.MapPath(brochurePath));
e.Values["brochurePath"] = brochurePath;
}
}
Шаг 8. ИсправлениеDisplayCategoryPicture.aspx
страницы
Давайте уделим немного времени, чтобы протестировать интерфейс для вставки и ItemInserting
обработчик событий, созданный на последних нескольких этапах.
UploadInDetailsView.aspx
Посетите страницу через браузер и попытайтесь добавить категорию, но опустить рисунок или указать рисунок, отличный от JPG, или брошюру, отличной от PDF. В любом из этих случаев отобразится сообщение об ошибке и отменен рабочий процесс вставки.
Рис. 9. Предупреждение отображается, если отправлен недопустимый тип файла (щелкните, чтобы просмотреть изображение полного размера)
Убедившись, что страница требует загрузки изображения и не будет принимать файлы, отличные от PDF или не JPG, добавьте новую категорию с допустимым изображением JPG, оставив поле брошюры пустым. После нажатия кнопки "Вставка" страница будет обновлена, и новая запись будет добавлена в таблицу Categories
с двоичными данными загруженного изображения, хранящимися прямо в базе данных. GridView обновляется и отображает строку для новой добавленной категории, но, как показано на рисунке 10, изображение новой категории не отображается правильно.
Рис. 10. Изображение новой категории не отображается (щелкните, чтобы просмотреть изображение полного размера)
Причина, по которой новый рисунок не отображается, заключается в том, что страница DisplayCategoryPicture.aspx
, возвращающая изображение указанной категории, настроена на обработку растровых изображений, имеющих заголовок OLE. Этот 78-байтовый заголовок удаляется из двоичного содержимого столбца Picture
перед отправкой обратно клиенту. Но только что загруженный файл JPG для новой категории не имеет этого заголовка OLE; Поэтому допустимые, необходимые байты удаляются из двоичных данных образа.
Так как в Categories
таблице теперь есть и растровые изображения с заголовками OLE, и JPG, необходимо обновить DisplayCategoryPicture.aspx
, чтобы она выполняла удаление заголовков OLE для исходных восьми категорий и пропускала это удаление для новых записей категорий. В следующем руководстве мы рассмотрим, как обновить существующий образ записи, и мы обновим все старые изображения категорий, чтобы они были JPG. Пока используйте следующий код в DisplayCategoryPicture.aspx
, чтобы удалить заголовки OLE только для тех исходных восьми категорий.
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];
if (categoryID <= 8)
{
// For older categories, we must strip the OLE header... images are bitmaps
// 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);
}
else
{
// For new categories, images are JPGs...
// Output HTTP headers providing information about the binary data
Response.ContentType = "image/jpeg";
// Output the binary data
Response.BinaryWrite(category.Picture);
}
}
При этом изменении изображение JPG теперь отображается правильно в GridView.
Рис. 11. Изображения JPG для новых категорий правильно отрисовываются (щелкните, чтобы просмотреть изображение полного размера)
Шаг 9. Удаление брошюры в случае исключения
Одна из проблем хранения двоичных данных на файловой системе веб-сервера заключается в том, что она представляет отключение между моделью данных и его двоичными данными. Поэтому при удалении записи соответствующие двоичные данные в файловой системе также должны быть удалены. Это также может сыграть роль при вставке. Рассмотрим следующий сценарий: пользователь добавляет новую категорию, указывая допустимую фотографию и брошюру. При нажатии кнопки "Вставка" происходит обратная передача и срабатывает событие DetailsView ItemInserting
, чтобы сохранить брошюру в файловой системе веб-сервера. Затем вызывается метод Insert()
объекта ObjectDataSource, который вызывает метод CategoriesBLL
класса InsertWithPicture
, который, в свою очередь, вызывает метод CategoriesTableAdapter
объекта InsertWithPicture
.
Теперь что произойдет, если база данных находится в автономном режиме или если в INSERT
инструкции SQL возникает ошибка? Очевидно, что операция вставки закончится ошибкой, поэтому новая строка категории не будет добавлена в базу данных. Но у нас по-прежнему есть загруженный файл брошюры, сидящий на файловой системе веб-сервера! Этот файл необходимо удалить при возникновении исключения в процессе вставки.
Как описано ранее в руководстве по обработке BLL и DAL-Level исключения в руководстве по ASP.NET странице , при возникновении исключения из глубины архитектуры он пузырится через различные слои. На уровне презентации можно определить, произошло ли исключение из события DetailsView ItemInserted
. Этот обработчик событий также предоставляет значения объектов ObjectDataSource InsertParameters
. Поэтому можно создать обработчик событий для ItemInserted
события, который проверяет наличие исключения, и, если да, удаляет файл, указанный параметром ObjectDataSource brochurePath
:
protected void NewCategory_ItemInserted
(object sender, DetailsViewInsertedEventArgs e)
{
if (e.Exception != null)
{
// Need to delete brochure file, if it exists
if (e.Values["brochurePath"] != null)
System.IO.File.Delete(Server.MapPath(
e.Values["brochurePath"].ToString()));
}
}
Сводка
Существует несколько шагов, которые необходимо выполнить для предоставления веб-интерфейса для добавления записей, включающих двоичные данные. Если двоичные данные хранятся непосредственно в базе данных, скорее всего, вам потребуется обновить архитектуру, добавив конкретные методы для обработки ситуации вставки двоичных данных. После обновления архитектуры следующий шаг создает интерфейс вставки, который можно выполнить с помощью DetailsView, который был настроен для включения элемента управления FileUpload для каждого поля двоичных данных. Затем отправленные данные можно сохранить в файловой системе веб-сервера или назначить параметру источника данных в обработчике событий DetailsView ItemInserting
.
Сохранение двоичных данных в файловой системе требует большего планирования, чем сохранение данных непосредственно в базе данных. Чтобы избежать случая, когда загрузка одного пользователя перезаписывает загрузку другого, необходимо выбрать схему именования. Кроме того, необходимо предпринять дополнительные шаги, чтобы удалить загруженный файл, если вставка данных в базу данных завершится ошибкой.
Теперь у нас есть возможность добавлять новые категории в систему с помощью брошюры и рисунка, но мы еще не рассмотрели, как обновить двоичные данные существующей категории или как правильно удалить двоичные данные для удаленной категории. В следующем руководстве мы рассмотрим эти два раздела.
Счастливое программирование!
Сведения о авторе
Скотт Митчелл, автор семи книг ASP/ASP.NET и основатель 4GuysFromRolla.com, работает с технологиями Microsoft Web с 1998 года. Скотт работает независимым консультантом, тренером и писателем. Его последняя книга — Sams Teach Yourself ASP.NET 2.0 за 24 часа. С ним можно связаться по адресу mitchell@4GuysFromRolla.com.
Особое спасибо кому
Эта серия учебников была проверена многими полезными рецензентами. Основные рецензенты данного руководства были Дэйв Гарднер, Тереса Мерфи и Бернадетт Ли. Хотите просмотреть мои предстоящие статьи MSDN? Если да, напишите мне на mitchell@4GuysFromRolla.com.