Einschließen einer Dateiuploadoption beim Hinzufügen eines neuen Datensatzes (VB)
von Scott Mitchell
In diesem Tutorial wird gezeigt, wie Sie eine Weboberfläche erstellen, die es dem Benutzer ermöglicht, Sowohl Textdaten einzugeben als auch Binärdateien hochzuladen. Um die verfügbaren Optionen zum Speichern von Binärdaten zu veranschaulichen, wird eine Datei in der Datenbank gespeichert, während die andere im Dateisystem gespeichert wird.
Einführung
In den vorherigen beiden Tutorials haben wir Techniken zum Speichern von Binärdaten untersucht, die dem Datenmodell der Anwendung zugeordnet sind, uns mit der Verwendung des FileUpload-Steuerelements zum Senden von Dateien vom Client an den Webserver befasst und wie diese Binärdaten in einem Datenwebsteuerelement dargestellt werden. Wir müssen jedoch noch darüber sprechen, wie hochgeladene Daten dem Datenmodell zugeordnet werden.
In diesem Tutorial erstellen wir eine Webseite zum Hinzufügen einer neuen Kategorie. Zusätzlich zu TextBoxes für den Namen und die Beschreibung der Kategorie muss diese Seite zwei FileUpload-Steuerelemente enthalten, eines für das bild der neuen Kategorie und eines für die Broschüre. Das hochgeladene Bild wird direkt in der Spalte des neuen Datensatzes Picture
gespeichert, während die Broschüre im ~/Brochures
Ordner mit dem Pfad zu der Datei gespeichert wird, die in der Spalte des neuen Datensatzes BrochurePath
gespeichert ist.
Vor dem Erstellen dieser neuen Webseite müssen wir die Architektur aktualisieren. Die CategoriesTableAdapter
s Standard Abfrage ruft die Picture
Spalte nicht ab. Folglich verfügt die automatisch generierte Insert
Methode nur über Eingaben für die CategoryName
Felder , Description
und BrochurePath
. Daher müssen wir eine zusätzliche Methode im TableAdapter erstellen, die alle vier Categories
Felder einfordert. Die CategoriesBLL
Klasse in der Geschäftslogikebene muss ebenfalls aktualisiert werden.
Schritt 1: Hinzufügen einerInsertWithPicture
Methode zumCategoriesTableAdapter
Als sie im Tutorial Erstellen einer Datenzugriffsebene erstellt CategoriesTableAdapter
wurde, haben wir sie so konfiguriert, dass die Anweisungen , UPDATE
und DELETE
automatisch basierend auf der Standard Abfrage generiert INSERT
werden. Darüber hinaus haben wir den TableAdapter angewiesen, den DB Direct-Ansatz zu verwenden, der die Methoden Insert
, Update
und Delete
erstellt hat. Diese Methoden führen die automatisch generierten INSERT
- , UPDATE
- und DELETE
-Anweisungen aus und akzeptieren daher Eingabeparameter basierend auf den Spalten, die von der Standard-Abfrage zurückgegeben werden. Im Tutorial Hochladen von Dateien haben wir die CategoriesTableAdapter
Abfrage s Standard erweitert, um die BrochurePath
Spalte zu verwenden.
Da die CategoriesTableAdapter
s Standard Abfrage nicht auf die Picture
Spalte verweist, können wir weder einen neuen Datensatz hinzufügen noch einen vorhandenen Datensatz mit einem Wert für die Picture
Spalte aktualisieren. Um diese Informationen zu erfassen, können wir entweder eine neue Methode im TableAdapter erstellen, die speziell zum Einfügen eines Datensatzes mit Binärdaten verwendet wird, oder wir können die automatisch generierte INSERT
Anweisung anpassen. Das Problem beim Anpassen der automatisch generierten INSERT
Anweisung besteht darin, dass unsere Anpassungen vom Assistenten überschrieben werden. Angenommen, wir haben die INSERT
-Anweisung so angepasst, dass die Picture
-Spalte verwendet wird. Dadurch würde die TableAdapter-Methode Insert
aktualisiert, um einen zusätzlichen Eingabeparameter für die Binärdaten der Kategorie des Bilds einzuschließen. Wir könnten dann eine Methode in der Geschäftslogikebene erstellen, um diese DAL-Methode zu verwenden, und diese BLL-Methode über die Präsentationsebene aufrufen, und alles würde wunderbar funktionieren. Das heißt, bis wir den TableAdapter das nächste Mal über den TableAdapter-Konfigurations-Assistenten konfiguriert haben. Sobald der Assistent abgeschlossen war, wurden unsere Anpassungen an der INSERT
-Anweisung überschrieben, die Insert
Methode rückgängig machen in ihre alte Form, und unser Code würde nicht mehr kompiliert!
Hinweis
Dieses Ärgernis ist kein Problem, wenn gespeicherte Prozeduren anstelle von AD-hoc-SQL-Anweisungen verwendet werden. In einem zukünftigen Tutorial wird die Verwendung gespeicherter Prozeduren anstelle von Ad-hoc-SQL-Anweisungen in der Datenzugriffsebene untersucht.
Um diese potenziellen Kopfschmerzen zu vermeiden, lässt sich anstelle der Anpassung der automatisch generierten SQL-Anweisungen stattdessen eine neue Methode für den TableAdapter erstellen. Diese Methode mit dem Namen InsertWithPicture
akzeptiert Werte für die CategoryName
Spalten , Description
, BrochurePath
und Picture
führt eine INSERT
Anweisung aus, die alle vier Werte in einem neuen Datensatz speichert.
Öffnen Sie das typisierte DataSet, und klicken Sie im Designer mit der rechten Maustaste auf die CategoriesTableAdapter
Kopfzeile s, und wählen Sie im Kontextmenü Abfrage hinzufügen aus. Dadurch wird der TableAdapter-Abfragekonfigurations-Assistent gestartet, der mit der Frage beginnt, wie die TableAdapter-Abfrage auf die Datenbank zugreifen soll. Wählen Sie SQL-Anweisungen verwenden aus, und klicken Sie auf Weiter. Im nächsten Schritt wird zur Eingabe des Typs der zu generierenden Abfrage aufgefordert. Da wir eine Abfrage erstellen, um der Categories
Tabelle einen neuen Datensatz hinzuzufügen, wählen Sie INSERT aus, und klicken Sie auf Weiter.
Abbildung 1: Auswählen der INSERT-Option (Klicken Sie hier, um das Bild in voller Größe anzuzeigen)
Wir müssen jetzt die SQL-Anweisung INSERT
angeben. Der Assistent schlägt automatisch eine INSERT
Anweisung vor, die der Standard abfrage von TableAdapter entspricht. In diesem Fall handelt es sich um eine INSERT
Anweisung, die die CategoryName
Werte , Description
und BrochurePath
einfügt. Aktualisieren Sie die -Anweisung, sodass die Picture
Spalte zusammen mit einem @Picture
Parameter enthalten ist, wie folgt:
INSERT INTO [Categories]
([CategoryName], [Description], [BrochurePath], [Picture])
VALUES
(@CategoryName, @Description, @BrochurePath, @Picture)
Der letzte Bildschirm des Assistenten fordert uns auf, die neue TableAdapter-Methode zu benennen. Geben Sie ein, InsertWithPicture
und klicken Sie auf Fertig stellen.
Abbildung 2: Name der neuen TableAdapter-Methode InsertWithPicture
(Klicken Sie hier, um das Bild in voller Größe anzuzeigen)
Schritt 2: Aktualisieren der Geschäftslogikebene
Da die Präsentationsebene nur eine Schnittstelle mit der Geschäftslogikebene herstellen sollte, anstatt sie zu umgehen, um direkt zur Datenzugriffsebene zu wechseln, müssen wir eine BLL-Methode erstellen, die die soeben erstellte DAL-Methode (InsertWithPicture
) aufruft. Erstellen Sie für dieses Tutorial eine Methode mit dem Namen in der CategoriesBLL
-Klasse, die als Eingabe drei String
s und ein Byte
Array InsertWithPicture
akzeptiert. Die String
Eingabeparameter gelten für den Namen, die Beschreibung und den Pfad der Broschürendatei, während das Byte
Array für den binären Inhalt des Kategoriebilds gilt. Wie der folgende Code zeigt, ruft diese BLL-Methode die entsprechende DAL-Methode auf:
<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
Hinweis
Stellen Sie sicher, dass Sie das typisierte DataSet gespeichert haben, bevor Sie die InsertWithPicture
-Methode zur BLL hinzufügen. Da der CategoriesTableAdapter
Klassencode automatisch basierend auf dem typisierten DataSet generiert wird, weiß die -Eigenschaft nichts über die InsertWithPicture
-Methode, wenn Sie ihre Änderungen nicht zuerst am typisierten DataSet Adapter
speichern.
Schritt 3: Auflisten der vorhandenen Kategorien und deren Binärdaten
In diesem Tutorial erstellen wir eine Seite, die es einem Endbenutzer ermöglicht, dem System eine neue Kategorie hinzuzufügen und ein Bild und eine Broschüre für die neue Kategorie bereitzustellen. Im vorherigen Tutorial haben wir ein GridView mit einem TemplateField und ImageField verwendet, um den Namen, die Beschreibung, das Bild und einen Link zum Herunterladen der Broschüre für jede Kategorie anzuzeigen. Lassen Sie uns diese Funktionalität für dieses Tutorial replizieren und eine Seite erstellen, die alle vorhandenen Kategorien auflistet und das Erstellen neuer Kategorien zulässt.
Öffnen Sie zunächst die DisplayOrDownload.aspx
Seite aus dem BinaryData
Ordner. Wechseln Sie zur Quellansicht, und kopieren Sie die deklarative Syntax GridView und ObjectDataSource, und fügen Sie sie in das <asp:Content>
-Element in UploadInDetailsView.aspx
ein. Vergessen Sie auch nicht, die GenerateBrochureLink
-Methode aus der CodeBehind-Klasse von DisplayOrDownload.aspx
in zu kopieren UploadInDetailsView.aspx
.
Abbildung 3: Kopieren und Einfügen der deklarativen Syntax von DisplayOrDownload.aspx
in UploadInDetailsView.aspx
(Klicken Sie hier, um das Bild in voller Größe anzuzeigen)
Nachdem Sie die deklarative Syntax und GenerateBrochureLink
Methode auf die UploadInDetailsView.aspx
Seite kopiert haben, zeigen Sie die Seite über einen Browser an, um sicherzustellen, dass alles ordnungsgemäß kopiert wurde. Es sollte ein GridView-Objekt mit den acht Kategorien angezeigt werden, das einen Link zum Herunterladen der Broschüre sowie das Kategoriebild enthält.
Abbildung 4: Jede Kategorie sollte nun zusammen mit den zugehörigen Binärdaten angezeigt werden (Klicken Sie hier, um das Bild in voller Größe anzuzeigen)
Schritt 4: Konfigurieren von für die Unterstützung desCategoriesDataSource
Einfügens
Die CategoriesDataSource
von GridView Categories
verwendete ObjectDataSource bietet derzeit keine Möglichkeit zum Einfügen von Daten. Um das Einfügen über dieses Datenquellensteuerelement zu unterstützen, müssen wir die Insert
-Methode einer -Methode im zugrunde liegenden Objekt CategoriesBLL
zuordnen. Insbesondere möchten wir sie der Methode zuordnen, die CategoriesBLL
wir in Schritt 2 hinzugefügt haben. InsertWithPicture
Klicken Sie zunächst im Smarttag ObjectDataSource auf den Link Datenquelle konfigurieren. Auf dem ersten Bildschirm wird das Objekt angezeigt, mit dem die Datenquelle für die Arbeit konfiguriert ist. CategoriesBLL
Lassen Sie diese Einstellung unverändert, und klicken Sie auf Weiter, um zum Bildschirm Datenmethoden definieren zu gelangen. Wechseln Sie zur Registerkarte INSERT, und wählen Sie die InsertWithPicture
Methode aus der Dropdownliste aus. Klicken Sie auf Fertig stellen, um den Assistenten abzuschließen.
Abbildung 5: Konfigurieren der ObjectDataSource für die Verwendung der InsertWithPicture
-Methode (Klicken Sie hier, um das Bild in voller Größe anzuzeigen)
Hinweis
Nach Abschluss des Assistenten fragt Visual Studio möglicherweise, ob Sie Felder und Schlüssel aktualisieren möchten, wodurch die Datenwebsteuerelementfelder neu generiert werden. Wählen Sie Nein aus, da durch Auswahl von Ja alle Feldanpassungen überschrieben werden, die Sie möglicherweise vorgenommen haben.
Nach Abschluss des Assistenten enthält die ObjectDataSource nun einen Wert sowohl für ihre InsertMethod
Eigenschaft als InsertParameters
auch für die vier Kategoriespalten, wie im folgenden deklarativen Markup veranschaulicht:
<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>
Schritt 5: Erstellen der Einfügeschnittstelle
Wie zuerst in der Übersicht über das Einfügen, Aktualisieren und Löschen von Daten beschrieben, stellt das DetailsView-Steuerelement eine integrierte Einfügeschnittstelle bereit, die bei der Arbeit mit einem Datenquellensteuerelement verwendet werden kann, das das Einfügen unterstützt. Fügen Sie dieser Seite oberhalb von GridView ein DetailsView-Steuerelement hinzu, das die Einfügeschnittstelle dauerhaft rendert, sodass ein Benutzer schnell eine neue Kategorie hinzufügen kann. Nach dem Hinzufügen einer neuen Kategorie in detailsView wird die darunter liegende GridView automatisch aktualisiert und die neue Kategorie angezeigt.
Ziehen Sie zunächst ein DetailsView-Objekt aus der Toolbox auf die Designer oberhalb von GridView, legen Sie dessen ID
Eigenschaft auf NewCategory
fest und löschen Sie die Height
Eigenschaftswerte und Width
. Binden Sie es über das Smarttag DetailsView an das vorhandene CategoriesDataSource
, und aktivieren Sie dann das Kontrollkästchen Einfügen aktivieren.
Abbildung 6: Binden von DetailsView an die und Aktivieren von CategoriesDataSource
Einfügen (Klicken Sie hier, um das Bild in voller Größe anzuzeigen)
Legen Sie die -Eigenschaft auf fest, um die DetailsView in der Einfügeschnittstelle dauerhaft zu Insert
rendernDefaultMode
.
Beachten Sie, dass detailsView fünf BoundFields CategoryID
, CategoryName
, Description
, NumberOfProducts
und BrochurePath
enthält, obwohl das CategoryID
BoundField nicht in der Einfügeschnittstelle gerendert wird, da seine InsertVisible
-Eigenschaft auf False
festgelegt ist. Diese BoundFields sind vorhanden, da es sich um die Spalten handelt, die von der GetCategories()
-Methode zurückgegeben werden. Dies ist das, was die ObjectDataSource aufruft, um ihre Daten abzurufen. Für das Einfügen möchten wir jedoch nicht zulassen, dass der Benutzer einen Wert für NumberOfProducts
angeben kann. Darüber hinaus müssen wir ihnen erlauben, ein Bild für die neue Kategorie hochzuladen und ein PDF für die Broschüre hochzuladen.
Entfernen Sie das NumberOfProducts
BoundField-Element vollständig aus der DetailsView, und aktualisieren Sie dann die HeaderText
Eigenschaften von CategoryName
BrochurePath
und BoundFields in Kategorie bzw. Broschüre. Konvertieren Sie als Nächstes boundField BrochurePath
in ein TemplateField, und fügen Sie ein neues TemplateField für das Bild hinzu, sodass dieses neue TemplateField den HeaderText
Wert Picture erhält. Verschieben Sie templateField Picture
so, dass es sich zwischen TemplateField BrochurePath
und CommandField befindet.
Abbildung 7: Binden der DetailsView an die und Aktivieren des CategoriesDataSource
Einfügens
Wenn Sie das BoundField über das BrochurePath
Dialogfeld Felder bearbeiten in ein TemplateField konvertiert haben, enthält das TemplateField ein ItemTemplate
, EditItemTemplate
und InsertItemTemplate
. Nur die InsertItemTemplate
wird jedoch benötigt, also können Sie die anderen beiden Vorlagen entfernen. An diesem Punkt sollte Ihre deklarative Syntax von DetailsView wie folgt aussehen:
<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>
Hinzufügen von FileUpload-Steuerelementen für die Felder "Broschüre" und "Bild"
Derzeit enthält das BrochurePath
TemplateField s InsertItemTemplate
ein Textfeld, während das Picture
TemplateField keine Vorlagen enthält. Wir müssen diese beiden TemplateField s InsertItemTemplate
aktualisieren, um FileUpload-Steuerelemente zu verwenden.
Wählen Sie im Smarttag DetailsView die Option Vorlagen bearbeiten aus, und wählen Sie dann die BrochurePath
TemplateField s InsertItemTemplate
aus der Dropdownliste aus. Entfernen Sie das Textfeld, und ziehen Sie dann ein FileUpload-Steuerelement aus der Toolbox in die Vorlage. Legen Sie das FileUpload-Steuerelement s ID
auf fest BrochureUpload
. Auf ähnliche Weise fügen Sie dem Picture
TemplateField-Steuerelement InsertItemTemplate
ein FileUpload-Steuerelement hinzu. Legen Sie dieses FileUpload-Steuerelement auf ID
fest PictureUpload
.
Abbildung 8: Hinzufügen eines FileUpload-Steuerelements zu dem (Klicken Sie, um dasInsertItemTemplate
Bild in voller Größe anzuzeigen)
Nachdem Sie diese Ergänzungen gemacht haben, lautet die deklarative Syntax von TemplateField wie folgt:
<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>
Wenn ein Benutzer eine neue Kategorie hinzufügt, möchten wir sicherstellen, dass die Broschüre und das Bild den richtigen Dateityp aufweisen. Für die Broschüre muss der Benutzer ein PDF bereitstellen. Für das Bild muss der Benutzer eine Bilddatei hochladen, aber erlauben wir eine Bilddatei oder nur Bilddateien eines bestimmten Typs, z. B. GIFs oder JPGs? Um verschiedene Dateitypen zuzulassen, müssen wir das Categories
Schema erweitern, um eine Spalte einzuschließen, die den Dateityp erfasst, damit dieser Typ an den Client gesendet werden kann.Response.ContentType
DisplayCategoryPicture.aspx
Da wir keine solche Spalte haben, wäre es ratsam, Benutzer darauf zu beschränken, nur einen bestimmten Imagedateityp bereitzustellen. Die Categories
vorhandenen Bilder der Tabelle sind Bitmaps, aber JPGs sind ein geeigneteres Dateiformat für Bilder, die über das Web bereitgestellt werden.
Wenn ein Benutzer einen falschen Dateityp hochlädt, müssen wir den Einfügevorgang abbrechen und eine Meldung anzeigen, die das Problem anzeigt. Fügen Sie unter der DetailsView ein Label Web-Steuerelement hinzu. Legen Sie die ID
-Eigenschaft auf UploadWarning
fest, löschen Sie die Text
-Eigenschaft, legen Sie die CssClass
-Eigenschaft auf Warnung und die Visible
-Eigenschaft und EnableViewState
auf fest False
. Die Warning
CSS-Klasse wird in Styles.css
definiert und rendert den Text in einer großen, roten, kursiv formatierten, fetten Schriftart.
Hinweis
Idealerweise werden die CategoryName
BoundFields und Description
in TemplateFields konvertiert und ihre Einfügeschnittstellen angepasst. Die Description
Einfügeschnittstelle eignet sich beispielsweise wahrscheinlich besser für ein mehrzeiliges Textfeld. Da die CategoryName
Spalte keine Werte akzeptiert NULL
, sollte ein RequiredFieldValidator hinzugefügt werden, um sicherzustellen, dass der Benutzer einen Wert für den Namen der neuen Kategorie bereitstellt. Diese Schritte werden dem Leser als Übung überlassen. Ausführliche Informationen zum Erweitern der Datenänderungsschnittstelle finden Sie unter Anpassen der Datenänderungsschnittstellen .
Schritt 6: Speichern der hochgeladenen Broschüre im Dateisystem des Webservers
Wenn der Benutzer die Werte für eine neue Kategorie eingibt und auf die Schaltfläche Einfügen klickt, erfolgt ein Postback, und der Einfügeworkflow wird entfaltet. Zunächst wird das DetailView-EreignisItemInserting
ausgelöst. Als Nächstes wird die ObjectDataSource-Methode Insert()
aufgerufen, was dazu führt, dass der Categories
Tabelle ein neuer Datensatz hinzugefügt wird. Danach wird das DetailsView-Ereignis ItemInserted
ausgelöst.
Bevor die ObjectDataSource-Methode Insert()
aufgerufen wird, müssen wir zunächst sicherstellen, dass die entsprechenden Dateitypen vom Benutzer hochgeladen wurden, und dann die Broschüren-PDF im Dateisystem des Webservers speichern. Erstellen Sie einen Ereignishandler für das DetailView-Ereignis, ItemInserting
und fügen Sie den folgenden Code hinzu:
' 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
Der Ereignishandler verweist zunächst auf das BrochureUpload
FileUpload-Steuerelement aus den DetailView-Vorlagen. Wenn dann eine Broschüre hochgeladen wurde, wird die hochgeladene Dateierweiterung überprüft. Wenn die Erweiterung nicht .PDF ist, wird eine Warnung angezeigt, der Einfügevorgang wird abgebrochen, und die Ausführung des Ereignishandlers endet.
Hinweis
Die Verwendung der hochgeladenen Dateierweiterung ist keine sichere Technik, um sicherzustellen, dass es sich bei der hochgeladenen Datei um ein PDF-Dokument handelt. Der Benutzer könnte über ein gültiges PDF-Dokument mit der Erweiterung .Brochure
verfügen oder ein Nicht-PDF-Dokument genommen und ihm eine .pdf
Erweiterung geben. Der binärinhalt der Datei muss programmgesteuert untersucht werden, um den Dateityp schlüssiger überprüfen zu können. Solche gründlichen Ansätze sind jedoch oft überqualifizierung; die Überprüfung, ob die Erweiterung für die meisten Szenarien ausreichend ist.
Wie im Tutorial Hochladen von Dateien erläutert, muss beim Speichern von Dateien im Dateisystem darauf geachtet werden, dass der Upload eines Benutzers keinen anderen s überschreibt. In diesem Tutorial versuchen wir, denselben Namen wie die hochgeladene Datei zu verwenden. Wenn im Verzeichnis jedoch bereits eine Datei mit demselben ~/Brochures
Dateinamen vorhanden ist, fügen wir am Ende eine Zahl an, bis ein eindeutiger Name gefunden wird. Wenn der Benutzer beispielsweise eine Broschürendatei mit dem Namen Meats.pdf
hochlädt, aber bereits eine Datei mit dem ~/Brochures
Namen Meats.pdf
im Ordner vorhanden ist, ändern wir den gespeicherten Dateinamen in Meats-1.pdf
. Wenn dies vorhanden ist, versuchen Meats-2.pdf
wir , usw., bis ein eindeutiger Dateiname gefunden wird.
Der folgende Code verwendet die File.Exists(path)
-Methode , um zu ermitteln, ob bereits eine Datei mit dem angegebenen Dateinamen vorhanden ist. Wenn ja, werden weiterhin neue Dateinamen für die Broschüre ausprobiert, bis kein Konflikt gefunden wird.
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
Sobald ein gültiger Dateiname gefunden wurde, muss die Datei im Dateisystem gespeichert werden, und der Wert von brochurePath``InsertParameter
ObjectDataSource muss aktualisiert werden, damit dieser Dateiname in die Datenbank geschrieben wird. Wie wir im Tutorial Hochladen von Dateien gezeigt haben, kann die Datei mit der Methode des FileUpload-Steuerelements SaveAs(path)
gespeichert werden. Um den ObjectDataSource-Parameter zu brochurePath
aktualisieren, verwenden Sie die e.Values
Auflistung.
' Save the file to disk and set the value of the brochurePath parameter
BrochureUpload.SaveAs(Server.MapPath(brochurePath))
e.Values("brochurePath") = brochurePath
Schritt 7: Speichern des hochgeladenen Bilds in der Datenbank
Um das hochgeladene Bild im neuen Categories
Datensatz zu speichern, müssen wir den hochgeladenen Binärinhalt dem ObjectDataSource-Parameter picture
im DetailsView-Ereignis ItemInserting
zuweisen. Bevor wir diese Zuweisung vornehmen, müssen wir jedoch zuerst sicherstellen, dass es sich bei dem hochgeladenen Bild um ein JPG und nicht um einen anderen Bildtyp handelt. Wie in Schritt 6 können Sie die Dateierweiterung des hochgeladenen Bilds verwenden, um den Typ zu ermitteln.
Während die Categories
Tabelle Werte für die Picture
Spalte zulässtNULL
, verfügen derzeit alle Kategorien über ein Bild. Erzwingt den Benutzer, ein Bild anzugeben, wenn eine neue Kategorie über diese Seite hinzugefügt wird. Mit dem folgenden Code wird überprüft, ob ein Bild hochgeladen wurde und ob es über eine entsprechende Erweiterung verfügt.
' 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
Dieser Code sollte vor dem Code aus Schritt 6 platziert werden, damit bei einem Problem mit dem Bildupload der Ereignishandler beendet wird, bevor die Broschürendatei im Dateisystem gespeichert wird.
Wenn eine entsprechende Datei hochgeladen wurde, weisen Sie den hochgeladenen binären Inhalt dem Wert des Bildparameters mit der folgenden Codezeile zu:
' Set the value of the picture parameter
e.Values("picture") = PictureUpload.FileBytes
Der vollständigeItemInserting
Ereignishandler
Aus Gründen der Vollständigkeit sehen Sie hier den ItemInserting
Ereignishandler in seiner Gesamtheit:
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
Schritt 8: Korrigieren derDisplayCategoryPicture.aspx
Seite
Nehmen wir uns einen Moment Zeit, um die Einfügeschnittstelle und ItemInserting
den Ereignishandler zu testen, die in den letzten Schritten erstellt wurden. Besuchen Sie die UploadInDetailsView.aspx
Seite über einen Browser, und versuchen Sie, eine Kategorie hinzuzufügen, aber lassen Sie das Bild aus, oder geben Sie ein Nicht-JPG-Bild oder eine Nicht-PDF-Broschüre an. In jedem dieser Fälle wird eine Fehlermeldung angezeigt, und der Einfügeworkflow wird abgebrochen.
Abbildung 9: Eine Warnmeldung wird angezeigt, wenn ein ungültiger Dateityp hochgeladen ist (Klicken Sie, um das Bild in voller Größe anzuzeigen)
Nachdem Sie überprüft haben, ob für die Seite ein Bild hochgeladen werden muss und keine Nicht-PDF- oder Nicht-JPG-Dateien akzeptiert werden, fügen Sie eine neue Kategorie mit einem gültigen JPG-Bild hinzu, sodass das Feld Broschüre leer bleibt. Nachdem Sie auf die Schaltfläche Einfügen geklickt haben, wird die Seite postbacken, und ein neuer Datensatz wird der Tabelle hinzugefügt, wobei der Categories
binäre Inhalt des hochgeladenen Bilds direkt in der Datenbank gespeichert ist. Die GridView wird aktualisiert und zeigt eine Zeile für die neu hinzugefügte Kategorie an, aber wie Abbildung 10 zeigt, wird das Bild der neuen Kategorie nicht ordnungsgemäß gerendert.
Abbildung 10: Das Neue Kategoriebild wird nicht angezeigt (Klicken Sie hier, um das Bild in voller Größe anzuzeigen)
Der Grund, warum das neue Bild nicht angezeigt wird, ist, weil die Seite, die DisplayCategoryPicture.aspx
ein angegebenes Kategoriebild zurückgibt, für die Verarbeitung von Bitmaps mit einem OLE-Header konfiguriert ist. Dieser 78-Byte-Header wird aus dem binären Inhalt der Picture
Spalte entfernt, bevor er an den Client zurückgesendet wird. Aber die JPG-Datei, die wir gerade für die neue Kategorie hochgeladen haben, hat nicht diesen OLE-Header; daher werden gültige, erforderliche Bytes aus den Binärdaten des Images entfernt.
Da es jetzt sowohl Bitmaps mit OLE-Headern als auch JPGs in der Categories
Tabelle gibt, müssen wir aktualisieren DisplayCategoryPicture.aspx
, damit der OLE-Header für die ursprünglichen acht Kategorien entfernt wird und diese Stripping für die neueren Kategoriedatensätze umgangen wird. In unserem nächsten Tutorial untersuchen wir, wie Sie ein vorhandenes Datensatzimage aktualisieren, und wir aktualisieren alle alten Kategoriebilder so, dass es sich um JPGs handelt. Verwenden Sie jedoch vorerst den folgenden Code, DisplayCategoryPicture.aspx
um die OLE-Header nur für diese ursprünglichen acht Kategorien zu entfernen:
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
Mit dieser Änderung wird das JPG-Bild jetzt ordnungsgemäß in GridView gerendert.
Abbildung 11: Die JPG-Bilder für neue Kategorien werden ordnungsgemäß gerendert (Klicken Sie, um das Bild in voller Größe anzuzeigen)
Schritt 9: Löschen der Broschüre angesichts einer Ausnahme
Eine der Herausforderungen beim Speichern von Binärdaten auf dem Dateisystem des Webservers besteht darin, dass eine Trennung zwischen dem Datenmodell und seinen Binärdaten eingeführt wird. Daher müssen beim Löschen eines Datensatzes auch die entsprechenden Binärdaten im Dateisystem entfernt werden. Dies kann auch beim Einfügen ins Spiel kommen. Betrachten Sie das folgende Szenario: Ein Benutzer fügt eine neue Kategorie hinzu, indem er ein gültiges Bild und eine gültige Broschüre angibt. Beim Klicken auf die Schaltfläche Einfügen erfolgt ein Postback, und das DetailView-Ereignis ItemInserting
wird ausgelöst, wodurch die Broschüre im Dateisystem des Webservers gespeichert wird. Als Nächstes wird die ObjectDataSource-Methode Insert()
aufgerufen, die die s-Methode der CategoriesBLL
Klasse InsertWithPicture
aufruft, die die CategoriesTableAdapter
s-Methode InsertWithPicture
aufruft.
Was geschieht nun, wenn die Datenbank offline ist oder wenn in der SQL-Anweisung INSERT
ein Fehler vorliegt? Offensichtlich schlägt insert fehl, sodass der Datenbank keine neue Kategoriezeile hinzugefügt wird. Aber wir haben immer noch die hochgeladene Broschürendatei auf dem Dateisystem des Webservers! Diese Datei muss angesichts einer Ausnahme während des Einfügeworkflows gelöscht werden.
Wie bereits im Tutorial Behandeln von BLL- und DAL-Level-Ausnahmen in einer ASP.NET Seite erläutert, wird eine Ausnahme aus den Tiefen der Architektur ausgelöst, die sich über die verschiedenen Ebenen befindet. Auf der Präsentationsebene können wir ermitteln, ob eine Ausnahme aus dem DetailView-Ereignis ItemInserted
aufgetreten ist. Dieser Ereignishandler stellt auch die Werte des ObjectDataSource-Objekts bereit InsertParameters
. Aus diesem Grund können wir einen Ereignishandler für das Ereignis erstellen, der ItemInserted
überprüft, ob eine Ausnahme vorliegt, und in diesem Fall die durch den ObjectDataSource-Parameter brochurePath
angegebene Datei löscht:
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
Zusammenfassung
Es gibt eine Reihe von Schritten, die ausgeführt werden müssen, um eine webbasierte Schnittstelle zum Hinzufügen von Datensätzen bereitzustellen, die Binärdaten enthalten. Wenn die Binärdaten direkt in der Datenbank gespeichert werden, müssen Sie wahrscheinlich die Architektur aktualisieren und bestimmte Methoden hinzufügen, um den Fall zu behandeln, in dem Binärdaten eingefügt werden. Nachdem die Architektur aktualisiert wurde, besteht der nächste Schritt darin, die Einfügeschnittstelle zu erstellen. Dies kann mithilfe einer DetailsView ausgeführt werden, die so angepasst wurde, dass es ein FileUpload-Steuerelement für jedes binäre Datenfeld enthält. Die hochgeladenen Daten können dann im Dateisystem des Webservers gespeichert oder einem Datenquellenparameter im DetailsView-Ereignishandler ItemInserting
zugewiesen werden.
Das Speichern von Binärdaten im Dateisystem erfordert mehr Planung als das direkte Speichern von Daten in der Datenbank. Es muss ein Benennungsschema ausgewählt werden, um zu vermeiden, dass der Upload eines Benutzers einen anderen s überschreibt. Außerdem müssen zusätzliche Schritte ausgeführt werden, um die hochgeladene Datei zu löschen, wenn die Datenbankeinfügung fehlschlägt.
Wir haben jetzt die Möglichkeit, dem System neue Kategorien mit einer Broschüre und einem Bild hinzuzufügen, aber wir müssen uns noch nicht ansehen, wie eine vorhandene Kategorie binärdaten aktualisiert oder wie die Binärdaten für eine gelöschte Kategorie ordnungsgemäß entfernt werden. Diese beiden Themen werden im nächsten Tutorial behandelt.
Viel Spaß beim Programmieren!
Zum Autor
Scott Mitchell, Autor von sieben ASP/ASP.NET-Büchern und Gründer von 4GuysFromRolla.com, arbeitet seit 1998 mit Microsoft-Webtechnologien. Scott arbeitet als unabhängiger Berater, Trainer und Autor. Sein neuestes Buch ist Sams Teach Yourself ASP.NET 2.0 in 24 Stunden. Er kann unter mitchell@4GuysFromRolla.comoder über seinen Blog erreicht werden, der unter http://ScottOnWriting.NETzu finden ist.
Besonderen Dank an
Diese Tutorialreihe wurde von vielen hilfreichen Prüfern überprüft. Leitende Prüfer für dieses Tutorial waren Dave Gardner, Teresa Murphy und Bernadette Leigh. Möchten Sie meine anstehenden MSDN-Artikel lesen? Wenn dies der Fall ist, legen Sie eine Zeile unter abmitchell@4GuysFromRolla.com.