Share via


Einschließen einer Dateiuploadoption beim Hinzufügen eines neuen Datensatzes (VB)

von Scott Mitchell

PDF herunterladen

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 CategoryNameFelder , Descriptionund 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 einerInsertWithPictureMethode zumCategoriesTableAdapter

Als sie im Tutorial Erstellen einer Datenzugriffsebene erstellt CategoriesTableAdapter wurde, haben wir sie so konfiguriert, dass die Anweisungen , UPDATEund DELETE automatisch basierend auf der Standard Abfrage generiert INSERTwerden. Darüber hinaus haben wir den TableAdapter angewiesen, den DB Direct-Ansatz zu verwenden, der die Methoden Insert, Updateund Deleteerstellt 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 InsertWithPictureakzeptiert Werte für die CategoryNameSpalten , Description, BrochurePathund 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.

Wählen Sie die INSERT-Option aus.

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 CategoryNameWerte , Descriptionund 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.

Nennen Sie die neue TableAdapter-Methode InsertWithPicture.

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.aspxein. Vergessen Sie auch nicht, die GenerateBrochureLink -Methode aus der CodeBehind-Klasse von DisplayOrDownload.aspx in zu kopieren UploadInDetailsView.aspx.

Kopieren und Einfügen der deklarativen Syntax aus DisplayOrDownload.aspx in 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.

Jede Kategorie sollte nun zusammen mit ihren Binärdaten angezeigt werden.

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 desCategoriesDataSourceEinfü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 CategoriesBLLzuordnen. 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.

Konfigurieren der ObjectDataSource für die Verwendung der InsertWithPicture-Methode

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.

Screenshot von DetailsView mit der CategoryID-Eigenschaft, die auf NewCategory festgelegt ist, die Werte der Height- und Width-Eigenschaft leer sind und das Kontrollkästchen Einfügen aktivieren aktiviert ist.

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 InsertrendernDefaultMode.

Beachten Sie, dass detailsView fünf BoundFields CategoryID, CategoryName, Description, NumberOfProductsund BrochurePath enthält, obwohl das CategoryID BoundField nicht in der Einfügeschnittstelle gerendert wird, da seine InsertVisible -Eigenschaft auf Falsefestgelegt 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 NumberOfProductsangeben 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 CategoryNameBrochurePath 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.

Screenshot des Feldfensters mit hervorgehobenem TemplateField, Picture und HeaderText

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, EditItemTemplateund 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 InsertItemTemplateein FileUpload-Steuerelement hinzu. Legen Sie dieses FileUpload-Steuerelement auf ID fest PictureUpload.

Hinzufügen eines FileUpload-Steuerelements zur InsertItemTemplate

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.ContentTypeDisplayCategoryPicture.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 UploadWarningfest, 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 .Brochureverfü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.pdfhochlä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.pdfwir , 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ändigeItemInsertingEreignishandler

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.aspxSeite

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.

Wenn ein ungültiger Dateityp hochgeladen wird, wird eine Warnmeldung angezeigt.

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.

Das Neue Kategoriebild wird nicht angezeigt.

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.

Die JPG-Bilder für neue Kategorien werden korrekt 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.