Freigeben über


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

von Scott Mitchell

PDF herunterladen

In diesem Tutorial wird gezeigt, wie Sie eine Weboberfläche erstellen, die es dem Benutzer ermöglicht, Textdaten einzugeben und 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 beiden vorherigen Tutorials haben wir Techniken zum Speichern von Binärdaten untersucht, die dem Datenmodell der Anwendung zugeordnet sind, uns mit dem FileUpload-Steuerelement zum Senden von Dateien vom Client an den Webserver befasst und wie diese binären Daten in einem Datenwebsteuerelement dargestellt werden. Wir müssen jedoch noch nicht darüber sprechen, wie hochgeladene Daten dem Datenmodell zugeordnet werden können.

In diesem Tutorial erstellen wir eine Webseite, um eine neue Kategorie hinzuzufügen. 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 in dem ~/Brochures Ordner gespeichert wird, wobei der Pfad zur Datei in der Spalte des neuen Datensatzes BrochurePath gespeichert wird.

Bevor Sie diese neue Webseite erstellen, müssen Sie 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 zur Eingabe aller vier Categories Felder auffordert. Die CategoriesBLL Klasse in der Geschäftslogikebene muss ebenfalls aktualisiert werden.

Schritt 1: Hinzufügen einerInsertWithPictureMethode zumCategoriesTableAdapter

Als wir die CategoriesTableAdapter Rückseite im Tutorial Erstellen einer Datenzugriffsebene erstellt haben, haben wir sie so konfiguriert, dass anweisungen automatisch generiert werdenINSERT, UPDATEund DELETE basierend auf der Standard Abfrage. 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 -Anweisungen aus DELETE und akzeptieren daher Eingabeparameter basierend auf den Spalten, die von der Standard-Abfrage zurückgegeben werden. Im Tutorial Zum 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ären Daten 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. Stellen Sie sich beispielsweise vor, wir haben die INSERT Anweisung so angepasst, dass sie die Verwendung der Picture Spalte einschließt. Dadurch würde die TableAdapter-Methode Insert so aktualisiert, dass sie einen zusätzlichen Eingabeparameter für die Binärdaten des Bilds der Kategorie einschließt. Wir könnten dann eine Methode in der Geschäftslogikschicht 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 über den TableAdapter-Konfigurations-Assistenten das nächste Mal konfiguriert haben. Sobald der Assistent abgeschlossen war, würden 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, sollten Sie statt die automatisch generierten SQL-Anweisungen anpassen, stattdessen eine neue Methode für den TableAdapter erstellen. Diese Methode mit dem Namen InsertWithPictureakzeptiert Werte für die CategoryNameSpalten , Description, BrochurePathund Picture und führt eine INSERT Anweisung aus, in der alle vier Werte in einem neuen Datensatz gespeichert werden.

Öffnen Sie das Typisierte DataSet, und klicken Sie im Designer mit der rechten Maustaste auf den CategoriesTableAdapter Header 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 OPTION EINFÜGEN aus.

Abbildung 1: Auswählen der OPTION EINFÜGEN (Klicken Sie hier, um das Bild in voller Größe anzuzeigen)

Wir müssen nun 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 so, dass die Picture Spalte zusammen mit einem @Picture Parameter enthalten ist, wie folgt:

INSERT INTO [Categories] 
    ([CategoryName], [Description], [BrochurePath], [Picture]) 
VALUES 
    (@CategoryName, @Description, @BrochurePath, @Picture)

Auf dem letzten Bildschirm des Assistenten werden wir aufgefordert, die neue TableAdapter-Methode zu nennen. Geben Sie ein InsertWithPicture , und klicken Sie auf Fertig stellen.

Nennen Sie die Neue TableAdapter-Methode InsertWithPicture.

Abbildung 2: Benennen Sie die neue 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 mit der Geschäftslogikschicht zusammenarbeiten sollte, anstatt sie zu umgehen, um direkt zur Datenzugriffsebene zu wechseln, müssen wir eine BLL-Methode erstellen, die die soeben erstellte DAL-Methode aufruft (InsertWithPicture). Erstellen Sie für dieses Tutorial eine Methode in der Klasse namensInsertWithPicture, die CategoriesBLL als Eingabe drei string s und ein byte Array akzeptiert. Die string Eingabeparameter gelten für den Namen, die Beschreibung und den Pfad der Broschürendatei der Kategorie, 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 void InsertWithPicture(string categoryName, string description, 
    string brochurePath, byte[] picture)
{
    Adapter.InsertWithPicture(categoryName, description, brochurePath, picture);
}

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 Typed 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 eine GridView mit einem TemplateField und ImageField verwendet, um den Namen, die Beschreibung, das Bild und einen Link zum Herunterladen der Broschüre 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 -Methode aus der GenerateBrochureLink CodeBehind-Klasse DisplayOrDownload.aspx in zu UploadInDetailsView.aspxzu kopieren.

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 eine GridView-Liste mit den acht Kategorien angezeigt werden, die einen Link zum Herunterladen der Broschüre sowie das Bild der Kategorie enthält.

Sie sollten nun jede Kategorie zusammen mit ihren Binärdaten sehen.

Abbildung 4: Jede Kategorie sollte nun zusammen mit ihren Binärdaten angezeigt werden (Klicken Sie hier, um das bild in voller Größe anzuzeigen)

Schritt 4: Konfigurieren von, um dasCategoriesDataSourceEinfügen zu unterstützen

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 zuordnen. CategoriesBLL 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 Verwendung 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 Die Auswahl von Ja alle feldspezifischen Anpassungen überschrieben werden, die Sie möglicherweise vorgenommen haben.

Nach Abschluss des Assistenten enthält die ObjectDataSource nun einen Wert für ihre InsertMethod Eigenschaft sowie InsertParameters für die vier Kategoriespalten, wie das folgende deklarative 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 beim Arbeiten mit einem Datenquellensteuerelement verwendet werden kann, das das Einfügen unterstützt. Fügen Sie dieser Seite über der GridView ein DetailsView-Steuerelement hinzu, das die Einfügeschnittstelle dauerhaft rendert, sodass ein Benutzer schnell eine neue Kategorie hinzufügen kann. Beim Hinzufügen einer neuen Kategorie in der DetailsView wird die darunter liegende GridView automatisch aktualisiert und die neue Kategorie angezeigt.

Ziehen Sie zunächst eine DetailsView aus der Toolbox auf den Designer über gridView, legen Sie ihre ID Eigenschaft auf NewCategory fest und löschen Sie die Height Eigenschaftenwerte und Width aus. Binden Sie es über das Smarttag von DetailsView an das vorhandene CategoriesDataSource , und aktivieren Sie dann das Kontrollkästchen Einfügen aktivieren.

Screenshot: Geöffnete DetailsAnsicht mit festgelegter CategoryID-Eigenschaft auf NewCategory, leeren Height- und Width-Eigenschaftswerten und aktiviertem Kontrollkästchen Einfügen aktivieren

Abbildung 6: Binden der DetailsAnsicht an das CategoriesDataSource und Aktivieren des Einfügens (Klicken, um das bild in voller Größe anzuzeigen)

Legen Sie die -Eigenschaft auf fest, um die DetailsView in der einfügenden Schnittstelle dauerhaft zu InsertrendernDefaultMode.

Beachten Sie, dass die DetailsView über fünf BoundFields CategoryID, CategoryName, , Description, NumberOfProductsund BrochurePath verfügt, obwohl das CategoryID BoundField in der einfügenden Schnittstelle nicht 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, was die ObjectDataSource aufruft, um ihre Daten abzurufen. Für das Einfügen soll der Benutzer jedoch keinen Wert für NumberOfProductsangeben. Darüber hinaus müssen wir es ihnen erlauben, ein Bild für die neue Kategorie hochzuladen sowie ein PDF für die Broschüre hochzuladen.

Entfernen Sie das NumberOfProducts BoundField vollständig aus der DetailsView, und aktualisieren Sie dann die HeaderText Eigenschaften von CategoryName und BrochurePath BoundFields in Category bzw. Brochure. Konvertieren Sie als Nächstes das BrochurePath BoundField 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 das Picture TemplateField so, dass es zwischen TemplateField BrochurePath und CommandField liegt.

Screenshot des Fensters

Abbildung 7: Binden von DetailsView an und Aktivieren des CategoriesDataSource Einfügens

Wenn Sie das BoundField über das BrochurePath Dialogfeld Felder bearbeiten in ein TemplateField-Objekt konvertiert haben, enthält templateField , ItemTemplateEditItemTemplateund InsertItemTemplate. Es wird jedoch nur der InsertItemTemplate benötigt, daher 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 TextBox-Element, 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 auf ID 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 zum InsertItemTemplate -Steuerelement (Klicken Sie hier, um das 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 Nutzer eine PDF-Datei angeben. Für das Bild muss der Benutzer eine Bilddatei hochladen, aber lassen wir eine Bilddatei oder nur Bilddateien eines bestimmten Typs zu, 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 Response.ContentType über in DisplayCategoryPicture.aspxgesendet werden kann. Da wir über keine solche Spalte verfügen, wäre es ratsam, Benutzer auf die Bereitstellung eines bestimmten Imagedateityps zu beschränken. 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 angibt. Fügen Sie ein Label Web-Steuerelement unterhalb von DetailsView hinzu. Legen Sie die -Eigenschaft auf UploadWarningfest, löschen Sie die Text -Eigenschaft, legen Sie die CssClass -Eigenschaft auf Warning und die Visible Eigenschaften und EnableViewState auf festfalse.ID Die Warning CSS-Klasse ist in Styles.css definiert und rendert den Text in einer großen, roten, kursiven, fett formatierten Schriftart.

Hinweis

Im Idealfall werden boundFields CategoryName und Description in TemplateFields konvertiert, und ihre Einfügeschnittstellen werden angepasst. Die Description Einfügeschnittstelle eignet sich 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änderungsschnittstelle .

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 DetailView-EreignisItemInserted 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 DetailsView-Ereignis, ItemInserting und fügen Sie den folgenden Code hinzu:

// Reference the FileUpload control
FileUpload BrochureUpload = 
    (FileUpload)NewCategory.FindControl("BrochureUpload");
if (BrochureUpload.HasFile)
{
    // Make sure that a PDF has been uploaded
    if (string.Compare(System.IO.Path.GetExtension
        (BrochureUpload.FileName), ".pdf", true) != 0)
    {
        UploadWarning.Text = 
            "Only PDF documents may be used for a category's brochure.";
        UploadWarning.Visible = true;
        e.Cancel = true;
        return;
    }
}

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, die Einfügung wird abgebrochen, und die Ausführung des Ereignishandlers wird beendet.

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 verwenden und ihm eine .pdf Erweiterung geben. Der binäre Inhalt der Datei müsste programmgesteuert untersucht werden, um den Dateityp abschließender überprüfen zu können. Solche gründlichen Ansätze sind jedoch oft überqualifizierung; Überprüfen, 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 nicht einen anderen s überschreibt. In diesem Tutorial versuchen wir, den gleichen 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 namens Meats.pdf im ~/Brochures Ordner vorhanden ist, ändern wir den gespeicherten Dateinamen in Meats-1.pdf. Wenn dies vorhanden ist, versuchen Meats-2.pdfwir, usw. so lange, bis ein eindeutiger Dateiname gefunden wird.

Der folgende Code verwendet die File.Exists(path) -Methode , um zu bestimmen, ob bereits eine Datei mit dem angegebenen Dateinamen vorhanden ist. Wenn dies der Grund ist, werden weiterhin neue Dateinamen für die Broschüre ausprobiert, bis kein Konflikt gefunden wird.

const string BrochureDirectory = "~/Brochures/";
string brochurePath = BrochureDirectory + BrochureUpload.FileName;
string fileNameWithoutExtension = 
    System.IO.Path.GetFileNameWithoutExtension(BrochureUpload.FileName);
int iteration = 1;
while (System.IO.File.Exists(Server.MapPath(brochurePath)))
{
    brochurePath = string.Concat(BrochureDirectory, 
        fileNameWithoutExtension, "-", iteration, ".pdf");
    iteration++;
}

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 gesehen 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ären Inhalt dem ObjectDataSource-Parameter picture im DetailsView s-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 für hochgeladene Bilder 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. Lassen Sie den Benutzer zwingen, ein Bild anzugeben, wenn eine neue Kategorie über diese Seite hinzugefügt wird. Der folgende Code überprüft, ob ein Bild hochgeladen wurde und ob es über eine entsprechende Erweiterung verfügt.

// Reference the FileUpload controls
FileUpload PictureUpload = (FileUpload)NewCategory.FindControl("PictureUpload");
if (PictureUpload.HasFile)
{
    // Make sure that a JPG has been uploaded
    if (string.Compare(System.IO.Path.GetExtension(PictureUpload.FileName), 
            ".jpg", true) != 0 &&
        string.Compare(System.IO.Path.GetExtension(PictureUpload.FileName), 
            ".jpeg", true) != 0)
    {
        UploadWarning.Text = 
            "Only JPG documents may be used for a category's picture.";
        UploadWarning.Visible = true;
        e.Cancel = true;
        return;
    }
}
else
{
    // No picture uploaded!
    UploadWarning.Text = 
        "You must provide a picture for the new category.";
    UploadWarning.Visible = true;
    e.Cancel = true;
    return;
}

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 Complete-EreignishandlerItemInserting

Aus Gründen der Vollständigkeit ist der ItemInserting Ereignishandler in seiner Gesamtheit aufgeführt:

protected void NewCategory_ItemInserting(object sender, DetailsViewInsertEventArgs e)
{
    // Reference the FileUpload controls
    FileUpload PictureUpload = (FileUpload)NewCategory.FindControl("PictureUpload");
    if (PictureUpload.HasFile)
    {
        // Make sure that a JPG has been uploaded
        if (string.Compare(System.IO.Path.GetExtension(PictureUpload.FileName), 
                ".jpg", true) != 0 &&
            string.Compare(System.IO.Path.GetExtension(PictureUpload.FileName), 
                ".jpeg", true) != 0)
        {
            UploadWarning.Text = 
                "Only JPG documents may be used for a category's picture.";
            UploadWarning.Visible = true;
            e.Cancel = true;
            return;
        }
    }
    else
    {
        // No picture uploaded!
        UploadWarning.Text = 
            "You must provide a picture for the new category.";
        UploadWarning.Visible = true;
        e.Cancel = true;
        return;
    }
    // Set the value of the picture parameter
    e.Values["picture"] = PictureUpload.FileBytes;
    
    
    // Reference the FileUpload controls
    FileUpload BrochureUpload = 
        (FileUpload)NewCategory.FindControl("BrochureUpload");
    if (BrochureUpload.HasFile)
    {
        // Make sure that a PDF has been uploaded
        if (string.Compare(System.IO.Path.GetExtension(BrochureUpload.FileName), 
            ".pdf", true) != 0)
        {
            UploadWarning.Text = 
                "Only PDF documents may be used for a category's brochure.";
            UploadWarning.Visible = true;
            e.Cancel = true;
            return;
        }
        const string BrochureDirectory = "~/Brochures/";
        string brochurePath = BrochureDirectory + BrochureUpload.FileName;
        string fileNameWithoutExtension = 
            System.IO.Path.GetFileNameWithoutExtension(BrochureUpload.FileName);
        int iteration = 1;
        while (System.IO.File.Exists(Server.MapPath(brochurePath)))
        {
            brochurePath = string.Concat(BrochureDirectory, fileNameWithoutExtension, 
                "-", iteration, ".pdf");
            iteration++;
        }
        // Save the file to disk and set the value of the brochurePath parameter
        BrochureUpload.SaveAs(Server.MapPath(brochurePath));
        e.Values["brochurePath"] = brochurePath;
    }
}

Schritt 8: Korrigieren derDisplayCategoryPicture.aspxSeite

Lassen Sie uns einen Moment zeit nehmen, 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 wird (Klicken Sie hier, um das Bild in voller Größe anzuzeigen)

Nachdem Sie überprüft haben, dass auf der 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 der Tabelle wird ein neuer Datensatz 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. Wie in Abbildung 10 dargestellt, wird das Bild der neuen Kategorie jedoch nicht ordnungsgemäß gerendert.

Das Bild der neuen Kategorie 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, dass die Seite, die DisplayCategoryPicture.aspx ein angegebenes Kategoriebild zurückgibt, so konfiguriert ist, dass Bitmaps verarbeitet werden, die über einen OLE-Header verfügen. 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 Bilds entfernt.

Da jetzt sowohl Bitmaps mit OLE-Headern als auch JPGs in der Categories Tabelle vorhanden sind, müssen wir aktualisieren DisplayCategoryPicture.aspx , damit das OLE-Header-Stripping für die ursprünglichen acht Kategorien ausgeführt wird und dieses 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 sie JPGs sind. Verwenden Sie vorerst jedoch den folgenden Code in DisplayCategoryPicture.aspx , um die OLE-Header nur für diese ursprünglichen acht Kategorien zu entfernen:

protected void Page_Load(object sender, EventArgs e)
{
    int categoryID = Convert.ToInt32(Request.QueryString["CategoryID"]);
    // Get information about the specified category
    CategoriesBLL categoryAPI = new CategoriesBLL();
    Northwind.CategoriesDataTable categories = 
        categoryAPI.GetCategoryWithBinaryDataByCategoryID(categoryID);
    Northwind.CategoriesRow category = categories[0];
    if (categoryID <= 8)
    {
        // For older categories, we must strip the OLE header... images are bitmaps
        // Output HTTP headers providing information about the binary data
        Response.ContentType = "image/bmp";
        // Output the binary data
        // But first we need to strip out the OLE header
        const int OleHeaderLength = 78;
        int strippedImageLength = category.Picture.Length - OleHeaderLength;
        byte[] strippedImageData = new byte[strippedImageLength];
        Array.Copy(category.Picture, OleHeaderLength, strippedImageData, 
            0, strippedImageLength);
        Response.BinaryWrite(strippedImageData);
    }
    else
    {
        // For new categories, images are JPGs...
        
        // Output HTTP headers providing information about the binary data
        Response.ContentType = "image/jpeg";
        // Output the binary data
        Response.BinaryWrite(category.Picture);
    }
}

Mit dieser Änderung wird das JPG-Bild jetzt in GridView ordnungsgemäß 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 hier, um das Bild in voller Größe anzuzeigen)

Schritt 9: Löschen der Broschüre im Angesicht einer Ausnahme

Eine der Herausforderungen beim Speichern von Binärdaten im Dateisystem des Webservers besteht darin, dass eine Trennung zwischen dem Datenmodell und seinen Binärdaten eingeführt wird. Daher müssen bei jedem 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 und gibt ein gültiges Bild und eine gültige Broschüre an. Wenn Sie auf die Schaltfläche Einfügen klicken, erfolgt ein Postback, und das DetailView-Ereignis ItemInserting wird ausgelöst, sodass die Broschüre im Dateisystem des Webservers gespeichert wird. Als Nächstes wird die ObjectDataSource-Methode Insert() aufgerufen, die die CategoriesBLL Klasse s-Methode 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 void NewCategory_ItemInserted
    (object sender, DetailsViewInsertedEventArgs e)
{
    if (e.Exception != null)
    {
        // Need to delete brochure file, if it exists
        if (e.Values["brochurePath"] != null)
            System.IO.File.Delete(Server.MapPath(
                e.Values["brochurePath"].ToString()));
    }
}

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.