Включение параметра отправки файла при добавлении новой записи (VB)
В этом руководстве показано, как создать веб-интерфейс, позволяющий пользователю вводить текстовые данные и отправлять двоичные файлы. Чтобы проиллюстрировать параметры хранения двоичных данных, один файл будет сохранен в базе данных, а другой — в файловой системе.
Введение
В предыдущих двух руководствах мы изучили методы хранения двоичных данных, связанных с моделью данных приложения, рассмотрели, как использовать элемент управления FileUpload для отправки файлов с клиента на веб-сервер, и узнали, как представить эти двоичные данные в веб-элементе управления данными. Однако мы еще не поговорим о том, как связать отправленные данные с моделью данных.
В этом руководстве мы создадим веб-страницу для добавления новой категории. В дополнение к TextBoxes для имени и описания категории эта страница должна содержать два элемента управления FileUpload, один для нового изображения категории и один для брошюры. Отправленный рисунок будет храниться непосредственно в столбце Picture
новой записи, а брошюра будет сохранена в папке ~/Brochures
с путем к файлу, сохраненном в столбце BrochurePath
новой записи.
Перед созданием этой новой веб-страницы необходимо обновить архитектуру. Запрос CategoriesTableAdapter
main не получает Picture
столбец. Следовательно, автоматически созданный Insert
метод имеет только входные данные для CategoryName
полей , Description
и BrochurePath
. Поэтому необходимо создать дополнительный метод в TableAdapter, который запрашивает все четыре Categories
поля. Класс CategoriesBLL
уровня бизнес-логики также потребуется обновить.
Шаг 1. ДобавлениеInsertWithPicture
метода вCategoriesTableAdapter
При создании обратного CategoriesTableAdapter
объекта в учебнике Создание уровня доступа к данным мы настроили его для автоматического создания INSERT
инструкций , UPDATE
и DELETE
на основе запроса main. Кроме того, мы поручили TableAdapter использовать подход DB Direct, который создал методы Insert
, Update
и Delete
. Эти методы выполняют автоматически созданные INSERT
инструкции , UPDATE
и DELETE
и, следовательно, принимают входные параметры на основе столбцов, возвращаемых запросом main. В учебнике Отправка файлов мы дополнили CategoriesTableAdapter
запрос main для использования столбца BrochurePath
.
CategoriesTableAdapter
Так как запрос main не ссылается на 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
, которая сохраняет все четыре значения в новой записи.
Откройте типизированный набор данных и в Designer щелкните правой кнопкой мыши заголовок CategoriesTableAdapter
и выберите в контекстном меню пункт Добавить запрос. В этом случае запускается мастер настройки запросов TableAdapter, который начинается с запроса TableAdapter, который начинается с запроса TableAdapter, который должен получить доступ к базе данных. Выберите Использовать инструкции SQL и нажмите кнопку Далее. На следующем шаге запрашивается тип создаваемого запроса. Так как мы повторно создадим запрос для добавления новой записи в таблицу Categories
, нажмите кнопку ВСТАВКА и нажмите кнопку Далее.
Рис. 1. Выбор параметра INSERT (щелкните для просмотра полноразмерного изображения)
Теперь необходимо указать инструкцию INSERT
SQL. Мастер автоматически предлагает инструкцию, соответствующую INSERT
запросу main 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
). В этом руководстве создайте метод в классе с именем InsertWithPicture
, который принимает в CategoriesBLL
качестве входных данных три String
и Byte
массив. Входные String
параметры относятся к имени категории, описанию и пути к файлу брошюры, а Byte
массив — для двоичного содержимого рисунка категории. Как показано в следующем коде, этот метод BLL вызывает соответствующий метод DAL:
<System.ComponentModel.DataObjectMethodAttribute _
(System.ComponentModel.DataObjectMethodType.Insert, False)> _
Public Sub InsertWithPicture(categoryName As String, description As String, _
brochurePath As String, picture() As Byte)
Adapter.InsertWithPicture(categoryName, description, brochurePath, picture)
End Sub
Примечание
Перед добавлением InsertWithPicture
метода в BLL убедитесь, что типизированный набор данных сохранен. CategoriesTableAdapter
Так как код класса создается автоматически на основе typed DataSet, если вы не сохраните изменения в typed DataSet, Adapter
свойство не будет знать о методе InsertWithPicture
.
Шаг 3. Перечисление существующих категорий и их двоичных данных
В этом руководстве мы создадим страницу, которая позволяет конечному пользователю добавить новую категорию в систему, предоставив изображение и брошюру для новой категории. В предыдущем руководстве мы использовали GridView с TemplateField и ImageField, чтобы отобразить имя, описание, изображение и ссылку для каждой категории для скачивания брошюры. Давайте реплицируем эту функцию для этого руководства, создав страницу, на которой перечислены все существующие категории и разрешено создавать новые.
Начните с открытия страницы DisplayOrDownload.aspx
из BinaryData
папки. Перейдите в представление Источник и скопируйте декларативный синтаксис GridView и ObjectDataSource, вставив его в <asp:Content>
элемент в UploadInDetailsView.aspx
. Кроме того, не забудьте скопировать GenerateBrochureLink
метод из класса кода программной DisplayOrDownload.aspx
части в 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
. Оставьте этот параметр как есть и нажмите кнопку Далее, чтобы перейти к экрану Определение методов данных. Перейдите на вкладку 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 с панели элементов на Designer над GridView, задав его свойству значение NewCategory
и очистив Height
значения свойств и Width
.ID
Из смарт-тега DetailsView привяжите его к существующемуCategoriesDataSource
, а затем проверка флажок Включить вставку.
Рис. 6. Привязка DetailsView к CategoriesDataSource
и включение вставки (щелкните для просмотра полноразмерного изображения)
Чтобы окончательно отобразить DetailsView в интерфейсе вставки, присвойте свойству DefaultMode
значение Insert
.
Обратите внимание, что DetailsView имеет пять BoundFields CategoryID
, CategoryName
, NumberOfProducts
Description
и BrochurePath
, хотя CategoryID
BoundField не отображается в интерфейсе вставки, так как его InsertVisible
свойству False
присвоено значение . Эти Поля BoundFields существуют, так как они являются столбцами, возвращаемыми методом GetCategories()
, который вызывает ObjectDataSource для получения своих данных. Однако для вставки не нужно разрешать пользователю указывать значение для NumberOfProducts
. Кроме того, мы должны позволить им загрузить фотографию для новой категории, а также загрузить PDF-файл для брошюры.
NumberOfProducts
Удалите BoundField из DetailsView, а затем обновите HeaderText
свойства CategoryName
и BrochurePath
BoundFields на Category и Буклет соответственно. Затем преобразуйте 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
время templateField InsertItemTemplate
содержит Элемент TextBox, а Picture
TemplateField не содержит никаких шаблонов. Нам нужно обновить эти два шаблона для InsertItemTemplate
использования элементов управления FileUpload.
В смарт-теге DetailsView выберите параметр Изменить шаблоны, а затем выберите BrochurePath
TemplateField из InsertItemTemplate
раскрывающегося списка. Удалите textBox и перетащите элемент управления FileUpload с панели элементов в шаблон. Задайте для элемента управления FileUpload значение ID
BrochureUpload
. Аналогичным образом добавьте элемент управления FileUpload в Picture
templateField .InsertItemTemplate
Задайте для этого элемента управления 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 Web под DetailsView. Присвойте свойству значение , очистите его Text
свойство, задайте CssClass
для свойства значение Warning, а Visible
свойства и EnableViewState
— значение False
.UploadWarning
ID
Класс Warning
CSS определяется в Styles.css
и отображает текст крупным красным курсивом, полужирным шрифтом.
Примечание
В идеале CategoryName
и Description
BoundFields следует преобразовать в TemplateFields и настроить их интерфейсы вставки. Например Description
, интерфейс вставки, скорее всего, лучше подходит для многострочного текстового поля. Так как CategoryName
столбец не принимает NULL
значения, необходимо добавить RequiredFieldValidator, чтобы пользователь предоставлял значение для имени новой категории. Эти действия остаются в качестве упражнения для читателя. Дополнительные сведения о дополнении интерфейсов изменения данных см. в статье Настройка интерфейса изменения данных.
Шаг 6. Сохранение отправленного буклета в файловой системе веб-сервера
Когда пользователь вводит значения для новой категории и нажимает кнопку Вставка, происходит обратная связь и разворачивается рабочий процесс вставки. Во-первых, срабатывает событие DetailsViewItemInserting
. Затем вызывается метод ObjectDataSource, Insert()
что приводит к добавлению новой записи в таблицу Categories
. После этого срабатывает событие DetailsViewItemInserted
.
Перед вызовом метода ObjectDataSource Insert()
необходимо сначала убедиться, что пользователь загрузил соответствующие типы файлов, а затем сохранить PDF-файл брошюры в файловой системе веб-сервера. Создайте обработчик событий для события DetailsView ItemInserting
и добавьте следующий код:
' Reference the FileUpload controls
Dim BrochureUpload As FileUpload = _
CType(NewCategory.FindControl("BrochureUpload"), FileUpload)
If BrochureUpload.HasFile Then
' Make sure that a PDF has been uploaded
If String.Compare(System.IO.Path.GetExtension _
(BrochureUpload.FileName), ".pdf", True) <> 0 Then
UploadWarning.Text = _
"Only PDF documents may be used for a category's brochure."
UploadWarning.Visible = True
e.Cancel = True
Exit Sub
End If
End If
Обработчик событий начинается с ссылки на BrochureUpload
элемент управления FileUpload из шаблонов DetailsView. Затем, если брошюра была загружена, проверяется расширение отправленного файла. Если расширение не .PDF, отображается предупреждение, вставка отменяется и выполнение обработчика событий завершается.
Примечание
Полагаться на расширение отправленного файла не является верным методом, чтобы убедиться, что отправленный файл является PDF-документом. Пользователь мог иметь допустимый PDF-документ с расширением .Brochure
или взять документ, отличный от PDF, и предоставить ему .pdf
расширение. Двоичное содержимое файла должно быть проверено программным способом для более точной проверки типа файла. Однако такие тщательные подходы часто являются чрезмерными; Проверки расширения достаточно для большинства сценариев.
Как описано в руководстве по отправке файлов , при сохранении файлов в файловой системе необходимо соблюдать осторожность, чтобы один пользователь не перезаписал другие файлы. В этом руководстве мы попытаемся использовать то же имя, что и отправленный файл. Однако если в ~/Brochures
каталоге уже существует файл с таким же именем, мы добавим число в конце, пока не будет найдено уникальное имя. Например, если пользователь отправляет файл брошюры с именем Meats.pdf
, но в ~/Brochures
папке уже есть файл с именем Meats.pdf
, мы изменим имя сохраненного файла на Meats-1.pdf
. Если это существует, мы будем пытаться Meats-2.pdf
, и т. д., пока не будет найдено уникальное имя файла.
В следующем кодеFile.Exists(path)
метод используется для определения того, существует ли файл с указанным именем. Если это так, он продолжает пробовать новые имена файлов для брошюры, пока конфликт не будет найден.
Const BrochureDirectory As String = "~/Brochures/"
Dim brochurePath As String = BrochureDirectory & BrochureUpload.FileName
Dim fileNameWithoutExtension As String = _
System.IO.Path.GetFileNameWithoutExtension(BrochureUpload.FileName)
Dim iteration As Integer = 1
While System.IO.File.Exists(Server.MapPath(brochurePath))
brochurePath = String.Concat(BrochureDirectory, _
fileNameWithoutExtension, "-", iteration, ".pdf")
iteration += 1
End While
После обнаружения допустимого имени файла файл необходимо сохранить в файловой системе и обновить значение ObjectDataSource brochurePath``InsertParameter
, чтобы это имя файла было записано в базу данных. Как мы видели в руководстве По отправке файлов , файл можно сохранить с помощью метода элемента управления FileUpload s SaveAs(path)
. Чтобы обновить параметр 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 s picture
в событии ItemInserting
DetailsView. Однако перед выполнением этого назначения необходимо сначала убедиться, что загруженное изображение является JPG, а не каким-либо другим типом изображения. Как и на шаге 6, давайте воспользуемся расширением файла загруженного изображения, чтобы определить его тип.
Хотя таблица Categories
допускает NULL
значения для столбца Picture
, все категории в настоящее время имеют рисунок. При добавлении новой категории на этой странице пользователь будет предоставлять изображение. Следующий код проверяет, что изображение отправлено и имеет соответствующее расширение.
' Reference the FileUpload controls
Dim PictureUpload As FileUpload = _
CType(NewCategory.FindControl("PictureUpload"), FileUpload)
If PictureUpload.HasFile Then
' Make sure that a JPG has been uploaded
If String.Compare(System.IO.Path.GetExtension(PictureUpload.FileName), _
".jpg", True) <> 0 AndAlso _
String.Compare(System.IO.Path.GetExtension(PictureUpload.FileName), _
".jpeg", True) <> 0 Then
UploadWarning.Text = _
"Only JPG documents may be used for a category's picture."
UploadWarning.Visible = True
e.Cancel = True
Exit Sub
End If
Else
' No picture uploaded!
UploadWarning.Text = _
"You must provide a picture for the new category."
UploadWarning.Visible = True
e.Cancel = True
Exit Sub
End If
Этот код следует поместить перед кодом из шага 6, чтобы при возникновении проблем с отправкой рисунка обработчик событий завершит работу перед сохранением файла брошюры в файловой системе.
Предположим, что был отправлен соответствующий файл, назначьте переданное двоичное содержимое значению параметра picture со следующей строкой кода:
' Set the value of the picture parameter
e.Values("picture") = PictureUpload.FileBytes
Обработчик событий CompleteItemInserting
Для полноты ItemInserting
ниже приведен обработчик событий в полном объеме:
Protected Sub NewCategory_ItemInserting _
(sender As Object, e As DetailsViewInsertEventArgs) _
Handles NewCategory.ItemInserting
' Reference the FileUpload controls
Dim PictureUpload As FileUpload = _
CType(NewCategory.FindControl("PictureUpload"), FileUpload)
If PictureUpload.HasFile Then
' Make sure that a JPG has been uploaded
If String.Compare(System.IO.Path.GetExtension(PictureUpload.FileName), _
".jpg", True) <> 0 AndAlso _
String.Compare(System.IO.Path.GetExtension(PictureUpload.FileName), _
".jpeg", True) <> 0 Then
UploadWarning.Text = _
"Only JPG documents may be used for a category's picture."
UploadWarning.Visible = True
e.Cancel = True
Exit Sub
End If
Else
' No picture uploaded!
UploadWarning.Text = _
"You must provide a picture for the new category."
UploadWarning.Visible = True
e.Cancel = True
Exit Sub
End If
' Set the value of the picture parameter
e.Values("picture") = PictureUpload.FileBytes
' Reference the FileUpload controls
Dim BrochureUpload As FileUpload = _
CType(NewCategory.FindControl("BrochureUpload"), FileUpload)
If BrochureUpload.HasFile Then
' Make sure that a PDF has been uploaded
If String.Compare(System.IO.Path.GetExtension(BrochureUpload.FileName), _
".pdf", True) <> 0 Then
UploadWarning.Text = _
"Only PDF documents may be used for a category's brochure."
UploadWarning.Visible = True
e.Cancel = True
Exit Sub
End If
Const BrochureDirectory As String = "~/Brochures/"
Dim brochurePath As String = BrochureDirectory & BrochureUpload.FileName
Dim fileNameWithoutExtension As String = _
System.IO.Path.GetFileNameWithoutExtension(BrochureUpload.FileName)
Dim iteration As Integer = 1
While System.IO.File.Exists(Server.MapPath(brochurePath))
brochurePath = String.Concat(BrochureDirectory, _
fileNameWithoutExtension, "-", iteration, ".pdf")
iteration += 1
End While
' Save the file to disk and set the value of the brochurePath parameter
BrochureUpload.SaveAs(Server.MapPath(brochurePath))
e.Values("brochurePath") = brochurePath
End If
End Sub
Шаг 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 Sub Page_Load(sender As Object, e As EventArgs) Handles Me.Load
Dim categoryID As Integer = Convert.ToInt32(Request.QueryString("CategoryID"))
' Get information about the specified category
Dim categoryAPI As New CategoriesBLL()
Dim categories As Northwind.CategoriesDataTable = _
categoryAPI.GetCategoryWithBinaryDataByCategoryID(categoryID)
Dim category As Northwind.CategoriesRow = categories(0)
If categoryID <= 8 Then
' 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 OleHeaderLength As Integer = 78
Dim strippedImageLength As Integer = _
category.Picture.Length - OleHeaderLength
Dim strippedImageData(strippedImageLength) As Byte
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)
End If
End Sub
Благодаря этому изменению изображение JPG теперь правильно отображается в GridView.
Рис. 11. Изображения JPG для новых категорий отображаются правильно (щелкните, чтобы просмотреть полноразмерное изображение)
Шаг 9. Удаление брошюры перед лицом исключения
Одна из проблем хранения двоичных данных в файловой системе веб-сервера заключается в том, что это вызывает разрыв между моделью данных и ее двоичными данными. Поэтому при удалении записи необходимо также удалять соответствующие двоичные данные в файловой системе. Это также может вступить в игру при вставке. Рассмотрим следующий сценарий: пользователь добавляет новую категорию, указывая допустимое изображение и брошюру. При нажатии кнопки Вставка происходит обратная связь и возникает событие DetailsView ItemInserting
, при этом брошюра сохраняется в файловой системе веб-сервера. Затем вызывается метод ObjectDataSource, Insert()
который вызывает CategoriesBLL
метод класса s InsertWithPicture
, который вызывает CategoriesTableAdapter
метод s InsertWithPicture
.
Что произойдет, если база данных находится в автономном режиме или в инструкции INSERT
SQL возникает ошибка? Очевидно, что инструкция INSERT завершится ошибкой, поэтому новая строка категории не будет добавлена в базу данных. Но у нас по-прежнему есть загруженный файл брошюры, расположенный в файловой системе веб-сервера! Этот файл необходимо удалить перед лицом исключения во время рабочего процесса вставки.
Как обсуждалось ранее в руководстве По обработке исключений BLL- и DAL-Level в ASP.NET Page , при возникновении исключения из глубин архитектуры оно перемещается по различным уровням. На уровне представления можно определить, произошло ли исключение из события DetailsView ItemInserted
. Этот обработчик событий также предоставляет значения объектов ObjectDataSource InsertParameters
. Таким образом, можно создать обработчик событий для ItemInserted
события, который проверяет наличие исключения и, если да, удаляет файл, указанный параметром ObjectDataSource:brochurePath
Protected Sub NewCategory_ItemInserted _
(sender As Object, e As DetailsViewInsertedEventArgs) _
Handles NewCategory.ItemInserted
If e.Exception IsNot Nothing Then
' Need to delete brochure file, if it exists
If e.Values("brochurePath") IsNot Nothing Then
System.IO.File.Delete(Server.MapPath _
(e.Values("brochurePath").ToString()))
End If
End If
End Sub
Сводка
Существует ряд шагов, которые необходимо выполнить, чтобы предоставить веб-интерфейс для добавления записей, включающих двоичные данные. Если двоичные данные хранятся непосредственно в базе данных, скорее всего, вам потребуется обновить архитектуру, добавив определенные методы для обработки ситуации, когда вставляются двоичные данные. После обновления архитектуры следующим шагом является создание интерфейса вставки, который можно выполнить с помощью DetailsView, который был настроен для включения элемента управления FileUpload для каждого поля двоичных данных. Отправленные данные затем можно сохранить в файловой системе веб-сервера или назначить параметру источника данных в обработчике ItemInserting
событий DetailsView.
Сохранение двоичных данных в файловой системе требует больше планирования, чем сохранение данных непосредственно в базе данных. Необходимо выбрать схему именования, чтобы избежать перезаписи одного пользователя. Кроме того, необходимо выполнить дополнительные действия для удаления отправленного файла, если не удается выполнить вставку базы данных.
Теперь у нас есть возможность добавлять в систему новые категории с помощью брошюры и рисунка, но мы еще не рассмотрели, как обновить существующие двоичные данные категории или как правильно удалить двоичные данные для удаленной категории. Мы рассмотрим эти две темы в следующем руководстве.
Счастливое программирование!
Об авторе
Скотт Митчелл (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.