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

Einleitung

In den beiden vorangegangenen Tutorials haben wir Techniken zum Speichern von Binärdaten untersucht, die dem Datenmodell der Anwendung zugeordnet sind, uns angesehen, wie das FileUpload-Steuerelement verwendet wird, um Dateien vom Client an den Webserver zu senden, und wie diese Binärdaten in einem Datenwebsteuerelement dargestellt werden. Wir müssen jedoch noch darüber sprechen, wie hochgeladene Daten mit dem Datenmodell verknüpft werden können.

In diesem Tutorial erstellen wir eine Webseite, um eine neue Kategorie hinzuzufügen. Zusätzlich zu den Textfeldern 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 Picture neuen Datensatzes gespeichert, während die Broschüre in einem ~/Brochures Ordner mit dem Pfad zur Datei gespeichert wird, der in der Spalte des BrochurePath neuen Datensatzes gespeichert ist.

Bevor wir diese neue Webseite erstellen, müssen wir die Architektur aktualisieren. Die CategoriesTableAdapter Hauptabfrage von s 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 anfordert. Die CategoriesBLL Klasse in der Geschäftslogikschicht muss ebenfalls aktualisiert werden.

Schritt 1: Hinzufügen einerInsertWithPictureMethode zumCategoriesTableAdapter

Als wir die CategoriesTableAdapter Rückseite im Tutorial Erstellen einer Datenzugriffsschicht erstellt haben, haben wir sie so konfiguriert, dass automatisch , INSERTund UPDATE Anweisungen basierend auf der Hauptabfrage generiert werdenDELETE. 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 INSERTUPDATE, und DELETE -Anweisungen aus und akzeptieren daher Eingabeparameter basierend auf den Spalten, die von der Hauptabfrage zurückgegeben werden. Im Tutorial zum Hochladen von Dateien haben wir die CategoriesTableAdapter Hauptabfrage von s erweitert, um die BrochurePath Spalte zu verwenden.

Da die CategoriesTableAdapter Hauptabfrage von s 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 die Gefahr besteht, dass unsere Anpassungen vom Assistenten überschrieben werden. Stellen Sie sich beispielsweise vor, dass wir die INSERT Anweisung so anpassen, dass sie die Verwendung der Picture Spalte einschließt. Dadurch wird die TableAdapter-Methode aktualisiert, Insert um einen zusätzlichen Eingabeparameter für die Binärdaten des Bildes der Kategorie einzuschließen. Wir könnten dann eine Methode in der Geschäftslogikschicht erstellen, um diese DAL-Methode zu verwenden und diese BLL-Methode über die Präsentationsschicht aufzurufen, und alles würde wunderbar funktionieren. Das heißt, bis zum nächsten Mal, wenn wir den TableAdapter über den TableAdapter-Konfigurations-Assistenten konfiguriert haben. Sobald der Assistent abgeschlossen war, wurden unsere Anpassungen an der INSERT Anweisung überschrieben, die Insert Methode wurde in ihre alte Form zurückgesetzt, und der Code wurde 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 von gespeicherten Prozeduren anstelle von Ad-hoc-SQL-Anweisungen in der Datenzugriffsschicht untersucht.

Um diese potenziellen Kopfschmerzen zu vermeiden, sollten Sie anstelle des Anpassens der automatisch generierten SQL-Anweisungen 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, die alle vier Werte in einem neuen Datensatz speichert.

Öffnen Sie das typisierte DataSet, 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 zunächst fragt, wie die TableAdapter-Abfrage auf die Datenbank zugreifen soll. Wählen Sie "SQL-Anweisungen verwenden" aus, und klicken Sie auf "Weiter". Der nächste Schritt fragt nach dem Typ der Abfrage, die generiert werden soll. 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

Abbildung 1: Wählen Sie die Option INSERT (Klicken Sie auf diese Schaltfläche, um das Bild in voller Größe anzuzeigen)

Jetzt müssen wir die INSERT SQL-Anweisung angeben. Der Assistent schlägt automatisch eine INSERT Anweisung vor, die der Hauptabfrage des TableAdapters 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)

Der letzte Bildschirm des Assistenten fordert uns auf, die neue TableAdapter-Methode zu benennen. Geben Sie die Eingabetaste InsertWithPicture ein, und klicken Sie auf "Fertig stellen".

Nennen Sie die neue TableAdapter-Methode InsertWithPicture

Abbildung 2: Benennen der neuen TableAdapter-Methode InsertWithPicture (Klicken Sie, um das Bild in voller Größe anzuzeigen)

Schritt 2: Aktualisieren der Geschäftslogikschicht

Da die Präsentationsschicht nur mit der Geschäftslogikschicht verbunden sein sollte, anstatt sie zu umgehen, um direkt zur Datenzugriffsschicht zu gelangen, 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 benannten Klasse, die CategoriesBLL als Eingabe drei InsertWithPicture s und ein string Array byte akzeptiert. Die string Eingabeparameter beziehen sich auf den Namen, die Beschreibung und den Dateipfad der Broschüre, während das byte Array für den binären Inhalt des Bildes der Kategorie steht. 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 der BLL hinzufügen. Da der CategoriesTableAdapter Klassencode basierend auf dem typisierten DataSet automatisch generiert wird, weiß die Adapter Eigenschaft nichts von der InsertWithPicture Methode, wenn Sie Ihre Änderungen am typisierten DataSet nicht zuerst speichern.

Schritt 3: Auflisten der vorhandenen Kategorien und ihrer Binärdaten

In diesem Tutorial erstellen wir eine Seite, die es einem Endbenutzer ermöglicht, dem System eine neue Kategorie hinzuzufügen, indem sie ein Bild und eine Broschüre für die neue Kategorie bereitstellt. Im vorherigen Tutorial haben wir eine GridView mit einem TemplateField und einem 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 sowohl alle vorhandenen Kategorien auflistet als auch das Erstellen neuer Kategorien ermöglicht.

Öffnen Sie zunächst die DisplayOrDownload.aspx Seite aus dem BinaryData Ordner. Wechseln Sie zur Quellansicht, kopieren Sie die deklarative Syntax von 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 Code-Behind-Klasse von DisplayOrDownload.aspx to zu UploadInDetailsView.aspxkopieren.

Kopieren Sie die deklarative Syntax, und fügen Sie sie aus DisplayOrDownload.aspx in UploadInDetailsView.aspx ein.

Abbildung 3: Kopieren der deklarativen Syntax von und Einfügen von DisplayOrDownload.aspx nach UploadInDetailsView.aspx (Klicken Sie auf diese Schaltfläche, 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 in einem Browser an, um sicherzustellen, dass alles ordnungsgemäß kopiert wurde. Es sollte eine GridView mit einer Auflistung der 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: Sie sollten nun jede Kategorie zusammen mit ihren Binärdaten sehen (Klicken Sie hier, um das Bild in voller Größe anzuzeigen)

Schritt 4: KonfigurierenCategoriesDataSourcedes Einfügens zur Unterstützung des Einfügens

Die CategoriesDataSource von der Categories GridView verwendete ObjectDataSource bietet derzeit nicht die Möglichkeit, Daten einzufügen. 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 es der Methode zuordnen, die CategoriesBLL wir in Schritt 2 hinzugefügt haben. InsertWithPicture

Klicken Sie zunächst auf den Link Datenquelle konfigurieren im Smarttag von ObjectDataSource. Der erste Bildschirm zeigt das Objekt, für das die Datenquelle konfiguriert ist, CategoriesBLL. Lassen Sie diese Einstellung as-is und klicken Sie auf Weiter, um zum Bildschirm Datenmethoden definieren zu gelangen. Wechseln Sie zur Registerkarte EINFÜGEN, und wählen Sie die InsertWithPicture Methode aus der Dropdown-Liste 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 auf diese Schaltfläche, 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 Felder der Daten-Websteuerelemente neu generiert werden. Wählen Sie "Nein" aus, da durch die Auswahl von "Ja" alle Feldanpassungen ü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 erstmals in einer Übersicht über das Einfügen, Aktualisieren und Löschen von Daten beschrieben, bietet das DetailsView-Steuerelement eine integrierte Einfügeschnittstelle, die beim Arbeiten mit einem Datenquellensteuerelement verwendet werden kann, das das Einfügen unterstützt. Fügen Sie dieser Seite oberhalb der GridView ein DetailsView-Steuerelement hinzu, das die Einfügeschnittstelle dauerhaft rendert, sodass ein Benutzer schnell eine neue Kategorie hinzufügen kann. Wenn Sie eine neue Kategorie in der DetailsView hinzufügen, wird die darunter liegende GridView automatisch aktualisiert und die neue Kategorie angezeigt.

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

Screenshot, der zeigt, wie DetailsView geöffnet ist, wobei die CategoryID-Eigenschaft auf NewCategory festgelegt ist, die Eigenschaftswerte Height und Width leer sind und das Kontrollkästchen

Abbildung 6: Binden Sie die DetailsView an die CategoriesDataSource und aktivieren Sie das Einfügen (Klicken Sie auf diese Schaltfläche, um das Bild in voller Größe anzuzeigen).

Um die DetailsView dauerhaft in der Einfügeschnittstelle zu rendern, legen Sie ihre DefaultMode Eigenschaft auf Insertfest.

Beachten Sie, dass die DetailsView über fünf BoundFields CategoryID, CategoryName, Description, verfügt, NumberOfProductsund BrochurePath 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, die von ObjectDataSource zum Abrufen der Daten aufgerufen wird. Beim Einfügen möchten wir den Benutzer jedoch nicht einen Wert für NumberOfProductsangeben lassen. Darüber hinaus müssen wir ihnen erlauben, ein Bild für die neue Kategorie 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 und CategoryNameBrochurePath BoundFields auf 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, indem Sie diesem neuen TemplateField den HeaderText Wert Picture zuweisen. Verschieben Sie das Picture TemplateField so, dass es sich zwischen TemplateField BrochurePath und CommandField befindet.

Screenshot des Feldfensters mit hervorgehobenen TemplateField, Picture und HeaderText.

Abbildung 7: Binden Sie die DetailsView an die CategoriesDataSource und aktivieren Sie das Einfügen

Wenn Sie das BrochurePath BoundField über das Dialogfeld "Felder bearbeiten" in ein TemplateField konvertiert haben, enthält das TemplateField die Tasten ItemTemplate, EditItemTemplateund InsertItemTemplate. Es wird jedoch nur das InsertItemTemplate benötigt, also zögern Sie nicht, die anderen beiden Vorlagen zu entfernen. An diesem Punkt sollte die 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 eine TextBox, während das Picture TemplateField keine Vorlagen enthält. Wir müssen diese beiden TemplateField-Steuerelemente InsertItemTemplate aktualisieren, um FileUpload-Steuerelemente zu verwenden.

Wählen Sie im Smarttag DetailsView s die Option Edit Templates (Vorlagen bearbeiten) aus, und wählen Sie dann in der Dropdownliste das BrochurePath TemplateField s InsertItemTemplate aus. Entfernen Sie das TextBox-Steuerelement, und ziehen Sie dann ein FileUpload-Steuerelement aus der Toolbox in die Vorlage. Legen Sie das FileUpload-Steuerelement s ID auf BrochureUploadfest. Fügen Sie auf ähnliche Weise ein FileUpload-Steuerelement zu TemplateField Picture s hinzu InsertItemTemplate. Legen Sie dieses FileUpload-Steuerelement s ID auf PictureUploadfest.

Hinzufügen eines FileUpload-Steuerelements zu InsertItemTemplate

Abbildung 8: Hinzufügen eines FileUpload-Steuerelements zum InsertItemTemplate (Klicken Sie auf diese Schaltfläche, um das Bild in voller Größe anzuzeigen)

Nachdem Sie diese Ergänzungen vorgenommen haben, lautet die deklarative Syntax der beiden 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 haben. Für die Broschüre muss der Benutzer ein PDF bereitstellen. Für das Bild muss der Benutzer eine Bilddatei hochladen, aber erlauben wir jede Bilddatei oder nur Bilddateien eines bestimmten Typs, wie z.B. GIFs oder JPGs? Um verschiedene Dateitypen zuzulassen, müssen wir das Categories Schema um eine Spalte erweitern, die den Dateityp erfasst, sodass dieser Typ über Response.ContentType in DisplayCategoryPicture.aspxan den Client gesendet werden kann. Da wir keine solche Spalte haben, wäre es ratsam, die Benutzer auf die Bereitstellung eines bestimmten Bilddateityps zu beschränken. Die Categories vorhandenen Bilder der Tabelle sind Bitmaps, aber JPGs sind ein geeigneteres Dateiformat für Bilder, die über das Internet bereitgestellt werden.

Wenn ein Benutzer einen falschen Dateityp hochlädt, müssen wir das Einfügen abbrechen und eine Meldung anzeigen, die auf das Problem hinweist. Fügen Sie unterhalb der DetailsView ein Label-Websteuerelement hinzu. Legen Sie die ID Eigenschaft auf UploadWarning, löschen Sie die Text Eigenschaft, legen Sie die CssClass Eigenschaft auf Warnung und die VisibleEnableViewState und-Eigenschaften auf falsefest. Die Warning CSS-Klasse ist in Styles.css definiert und rendert den Text in einer großen, roten, kursiven, fetten Schriftart.

Hinweis

Im Idealfall werden die CategoryName und Description BoundFields in TemplateFields konvertiert und ihre Einfügeschnittstellen angepasst. Die Description Einfügeschnittstelle wäre beispielsweise wahrscheinlich besser für ein mehrzeiliges Textfeld geeignet. Und 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 angibt. Diese Schritte werden dem Leser als Übung überlassen. Unter Anpassen der Datenänderungsschnittstelle finden Sie einen detaillierten Einblick in die Erweiterung 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, wird ein Postback ausgeführt, und der Einfügeworkflow wird entfaltet. Zuerst wird das DetailsView-Ereignis ausgelöstItemInserting. 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 ausgelöstItemInserted.

Bevor die Methode von Insert() ObjectDataSource 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 beginnt mit dem Verweis auf das BrochureUpload FileUpload-Steuerelement aus den Vorlagen von DetailsView. Wenn dann eine Broschüre hochgeladen wurde, wird die Erweiterung der hochgeladenen Datei untersucht. Wenn die Erweiterung nicht .PDF wird, wird eine Warnung angezeigt, die Einfügung wird abgebrochen und die Ausführung des Ereignishandlers wird beendet.

Hinweis

Sich auf die Erweiterung der hochgeladenen Datei zu verlassen, ist keine todsichere 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 er könnte ein Nicht-PDF-Dokument genommen und ihm eine .pdf Erweiterung zugewiesen haben. Der binäre Inhalt der Datei müsste programmgesteuert untersucht werden, um den Dateityp schlüssiger zu überprüfen. Solche gründlichen Ansätze sind jedoch oft übertrieben; Das Überprüfen der Erweiterung ist für die meisten Szenarien ausreichend.

Wie im Tutorial Hochladen von Dateien beschrieben, muss beim Speichern von Dateien im Dateisystem darauf geachtet werden, dass der Upload eines Benutzers nicht den eines anderen s überschreibt. Für dieses Tutorial werden wir versuchen, den gleichen Namen wie die hochgeladene Datei zu verwenden. Wenn jedoch bereits eine Datei mit demselben Dateinamen in dem ~/Brochures Verzeichnis vorhanden ist, hängen wir am Ende eine Zahl an, bis ein eindeutiger Name gefunden wird. Wenn der Benutzer z. B. eine Broschürendatei mit dem Namen Meats.pdfhochlädt, aber Meats.pdf bereits eine Datei mit dem Namen ~/Brochures im Ordner vorhanden ist, ändern wir den Namen der gespeicherten Datei in Meats-1.pdf. Wenn dies der Fall ist, versuchen wir es mit Meats-2.pdfund so weiter, bis ein eindeutiger Dateiname gefunden wird.

Im folgenden Code wird die File.Exists(path) Methode verwendet, um zu bestimmen, ob bereits eine Datei mit dem angegebenen Dateinamen vorhanden ist. Wenn dies der Fall ist, werden 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++;
}

Nachdem 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 bereits im Tutorial zum Hochladen von Dateien gesehen haben, kann die Datei mit der Methode des FileUpload-Steuerelements SaveAs(path) gespeichert werden. Verwenden Sie die brochurePath Auflistung, um den Parameter von e.Values ObjectDataSource zu aktualisieren.

// 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 Bildes in der Datenbank

Um das hochgeladene Bild im neuen Categories Datensatz zu speichern, müssen wir den hochgeladenen binären Inhalt dem Parameter s picture von ObjectDataSource im Ereignis DetailsView zuweisen ItemInserting . Bevor wir diese Zuordnung vornehmen, müssen wir jedoch zunächst sicherstellen, dass es sich bei dem hochgeladenen Bild um ein JPG und nicht um einen anderen Bildtyp handelt. Wie in Schritt 6 verwenden wir die Dateierweiterung des hochgeladenen Bildes, um seinen Typ zu ermitteln.

Während die Categories Tabelle Werte für die NULL Spalte zulässtPicture, haben alle Kategorien derzeit ein Bild. Zwingen wir den Benutzer, ein Bild anzugeben, wenn er eine neue Kategorie über diese Seite hinzufügt. Mit dem folgenden Code wird überprüft, ob ein Bild hochgeladen wurde und ü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 der Ereignishandler beendet wird, bevor die Broschürendatei im Dateisystem gespeichert wird, wenn ein Problem beim Hochladen von Bildern auftritt.

Unter der Annahme, dass eine entsprechende Datei hochgeladen wurde, weisen Sie den hochgeladenen Binärinhalt mit der folgenden Codezeile dem Wert des Bildparameters zu:

// Set the value of the picture parameter
e.Values["picture"] = PictureUpload.FileBytes;

Der Complete-EreignishandlerItemInserting

Der Vollständigkeit halber finden Sie hier den ItemInserting Ereignishandler in seiner Gesamtheit:

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: Reparieren 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. Rufen Sie die UploadInDetailsView.aspx Seite über einen Browser auf und versuchen Sie, eine Kategorie hinzuzufügen, aber lassen Sie das Bild weg, 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üge-Workflow abgebrochen.

Eine Warnmeldung wird angezeigt, wenn ein ungültiger Dateityp hochgeladen wird

Abbildung 9: Eine Warnmeldung wird angezeigt, wenn ein ungültiger Dateityp hochgeladen wird (Klicken Sie auf diese Schaltfläche, um das Bild in voller Größe anzuzeigen).

Wenn Sie sich vergewissert haben, dass 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 und lassen Sie das Feld "Broschüre" leer. Nachdem Sie auf die Schaltfläche Einfügen geklickt haben, wird die Seite zurückgesetzt und der Categories Tabelle wird ein neuer Datensatz mit dem binären Inhalt des hochgeladenen Bildes hinzugefügt, der 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 Bild der neuen Kategorie wird nicht angezeigt

Abbildung 10: Das Bild der neuen Kategorie wird nicht angezeigt (Klicken Sie auf diese Schaltfläche, um das Bild in voller Größe anzuzeigen)

Der Grund, warum das neue Bild nicht angezeigt wird, liegt darin, dass die Seite, die ein bestimmtes Bild der Kategorie zurückgibt, für die DisplayCategoryPicture.aspx Verarbeitung von Bitmaps konfiguriert ist, 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 diesen OLE-Header nicht; Daher werden gültige, notwendige 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 , sodass das OLE-Header-Stripping für die ursprünglichen acht Kategorien durchgeführt wird und dieses Stripping für die neueren Kategoriedatensätze umgangen wird. In unserem nächsten Tutorial untersuchen wir, wie man das Bild eines vorhandenen Datensatzes aktualisiert, und wir aktualisieren alle alten Kategoriebilder so, dass sie JPGs sind. Verwenden Sie jedoch vorerst den folgenden Code 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 nun korrekt in der GridView gerendert.

Die JPG-Bilder für neue Kategorien werden korrekt gerendert

Abbildung 11: Die JPG-Bilder für neue Kategorien werden korrekt gerendert (Klicken Sie auf diese Schaltfläche, um das Bild in voller Größe anzuzeigen)

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

Eine der Herausforderungen beim Speichern von Binärdaten im Dateisystem des Webservers besteht darin, dass es zu einer Trennung zwischen dem Datenmodell und seinen Binärdaten kommt. Daher müssen bei jedem Löschen eines Datensatzes auch die entsprechenden Binärdaten auf dem Dateisystem entfernt werden. Dies kann auch beim Einsetzen zum Tragen kommen. Stellen Sie sich folgendes Szenario vor: 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, wird ein Postback ausgeführt, und das Ereignis von ItemInserting DetailsView wird ausgelöst, wobei die Broschüre im Dateisystem des Webservers gespeichert wird. Als Nächstes wird die ObjectDataSource-Methode Insert() s aufgerufen, die die CategoriesBLL s-Methode der Klasse InsertWithPicture aufruft, die wiederum die CategoriesTableAdapter s-Methode aufruft InsertWithPicture .

Was passiert nun, wenn die Datenbank offline ist oder wenn in der INSERT SQL-Anweisung ein Fehler auftritt? Es ist klar, dass die INSERT-Funktion fehlschlägt, 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 gelöscht werden, da während des Einfüge-Workflows eine Ausnahme auftritt.

Wie bereits im Tutorial Behandeln von BLL- und DAL-Level Ausnahmen auf einer ASP.NET Seite erläutert, wird eine Ausnahme, wenn sie aus den Tiefen der Architektur ausgelöst wird, durch die verschiedenen Schichten nach oben geschleudert. Auf der Präsentationsebene können wir feststellen, ob durch das Ereignis von ItemInserted DetailsView eine Ausnahme aufgetreten ist. Dieser Ereignishandler stellt auch die Werte von ObjectDataSource s InsertParametersbereit. Daher können wir einen Ereignishandler für das Ereignis erstellen, der ItemInserted überprüft, ob eine Ausnahme aufgetreten ist, und wenn ja, die durch den Parameter von ObjectDataSource angegebene Datei löscht brochurePath :

protected void NewCategory_ItemInserted
    (object sender, DetailsViewInsertedEventArgs e)
{
    if (e.Exception != null)
    {
        // Need to delete brochure file, if it exists
        if (e.Values["brochurePath"] != null)
            System.IO.File.Delete(Server.MapPath(
                e.Values["brochurePath"].ToString()));
    }
}

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, die mithilfe einer DetailsView erreicht werden kann, die so angepasst wurde, dass sie 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 Ereignishandler von ItemInserting DetailsView 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 gewählt werden, um zu vermeiden, dass der Upload eines Benutzers den Upload eines anderen s überschreibt. Außerdem müssen zusätzliche Schritte unternommen werden, um die hochgeladene Datei zu löschen, wenn das Einfügen der Datenbank 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 ansehen, wie man die Binärdaten einer bestehenden Kategorie aktualisiert oder wie man die Binärdaten für eine gelöschte Kategorie korrekt entfernt. Wir werden diese beiden Themen im nächsten Tutorial untersuchen.

Glückliche Programmierung!

Zum Autor

Scott Mitchell, Autor von sieben ASP/ASP.NET Büchern und Gründer von 4GuysFromRolla.com, arbeitet seit 1998 mit Microsoft Web Technologies zusammen. Scott arbeitet als unabhängiger Berater, Trainer und Schriftsteller. Sein neuestes Buch ist Sams Teach Yourself ASP.NET 2.0 in 24 Stunden. Er kann bei mitchell@4GuysFromRolla.comerreicht werden.

Besonderer Dank an

Diese Lernprogrammreihe wurde von vielen hilfreichen Prüfern überprüft. Die Hauptrezensenten für dieses Tutorial waren Dave Gardner, Teresa Murphy und Bernadette Leigh. Möchten Sie meine bevorstehenden MSDN-Artikel überprüfen? Wenn ja, schicken Sie mir eine Nachricht an mitchell@4GuysFromRolla.com.