Delen via


Een optie voor het uploaden van bestanden opnemen bij het toevoegen van een nieuwe record (C#)

door Scott Mitchell

PDF downloaden

Deze zelfstudie laat zien hoe u een webinterface maakt waarmee de gebruiker zowel tekstgegevens kan invoeren als binaire bestanden kan uploaden. Ter illustratie van de opties die beschikbaar zijn voor het opslaan van binaire gegevens, wordt het ene bestand opgeslagen in de database terwijl het andere wordt opgeslagen in het bestandssysteem.

Introductie

In de vorige twee zelfstudies hebben we technieken verkend voor het opslaan van binaire gegevens die zijn gekoppeld aan het gegevensmodel van de toepassing, bekeken hoe u het Besturingselement FileUpload gebruikt om bestanden van de client naar de webserver te verzenden en zag hoe u deze binaire gegevens in een gegevenswebbeheer kunt presenteren. We hebben het nog niet gehad over het koppelen van geüploade gegevens aan het gegevensmodel.

In deze zelfstudie maken we een webpagina om een nieuwe categorie toe te voegen. Naast Tekstvaken voor de naam en beschrijving van de categorie moeten op deze pagina twee FileUpload-besturingselementen worden opgenomen voor de afbeelding van de nieuwe categorie en één voor de brochure. De geüploade afbeelding wordt rechtstreeks opgeslagen in de Picture kolom van het nieuwe record, terwijl de brochure wordt opgeslagen in de ~/Brochures map, met het pad naar het bestand dat is opgeslagen in de BrochurePath kolom van het nieuwe record.

Voordat u deze nieuwe webpagina maakt, moet u de architectuur bijwerken. De hoofdquery van CategoriesTableAdapter haalt de Picture kolom niet op. Daarom heeft de automatisch gegenereerde Insert methode alleen invoer voor de CategoryName, Descriptionen BrochurePath velden. Daarom moeten we een extra methode maken in de TableAdapter die om alle vier Categories de velden vraagt. De CategoriesBLL klasse in de bedrijfslogicalaag moet ook worden bijgewerkt.

Stap 1: EenInsertWithPicturemethode toevoegen aan deCategoriesTableAdapter

Toen we de CategoriesTableAdapter terug in de tutorial Een gegevenstoegangslaag creëren hebben gemaakt, hebben we deze geconfigureerd om automatisch INSERT, UPDATE en DELETE instructies te genereren op basis van de hoofdquery. Bovendien hebben we de TableAdapter geïnstrueerd om de DB Direct-benadering te gebruiken, die de methoden Insertheeft gemaakt , Updateen Delete. Met deze methoden worden de automatisch gegenereerde INSERT-instructies uitgevoerd en worden er daarom invoerparameters geaccepteerd op basis van de kolommen die door de hoofdquery worden geretourneerd. In de zelfstudie Bestanden uploaden hebben we de CategoriesTableAdapter hoofdquery uitgebreid om de BrochurePath kolom te gebruiken.

Omdat de CategoriesTableAdapter hoofdquery niet verwijst naar de Picture kolom, kunnen we geen nieuwe record toevoegen of een bestaande record bijwerken met een waarde voor de Picture kolom. Om deze informatie vast te leggen, kunnen we een nieuwe methode maken in de TableAdapter die specifiek wordt gebruikt voor het invoegen van een record met binaire gegevens of we kunnen de automatisch gegenereerde INSERT instructie aanpassen. Het probleem met het aanpassen van de automatisch gegenereerde INSERT instructie is dat we het risico lopen dat onze aanpassingen door de wizard worden overschreven. Bijvoorbeeld, stel dat we de INSERT instructie hebben aangepast om gebruik te maken van de Picture kolom. Hiermee wordt de TableAdapter's Insert-methode bijgewerkt met een extra invoerparameter voor de binaire gegevens van het afbeeldingsbestand van de categorie. We kunnen vervolgens een methode maken in de bedrijfslogicalaag om deze DAL-methode te gebruiken en deze BLL-methode aan te roepen via de presentatielaag en alles zou geweldig werken. Dat wil zeggen, tot de volgende keer dat we de TableAdapter hebben geconfigureerd met behulp van de TableAdapter-configuratiewizard. Zodra de wizard is voltooid, worden onze aanpassingen aan de INSERT instructie overschreven, wordt de methode teruggezet naar het Insert oude formulier en wordt onze code niet meer gecompileerd.

Opmerking

Deze ergernis is een niet-probleem bij het gebruik van opgeslagen procedures in plaats van ad-hoc SQL-instructies. In een toekomstige zelfstudie wordt het gebruik van opgeslagen procedures in plaats van ad-hoc SQL-instructies in de Data Access-laag verkend.

Om deze potentiële hoofdpijn te voorkomen, in plaats van de automatisch gegenereerde SQL-instructies aan te passen, maken we in plaats daarvan een nieuwe methode voor de TableAdapter. Deze methode, benoemd InsertWithPicture, accepteert waarden voor de CategoryName, Descriptionen BrochurePathkolommen en Picture voert een INSERT instructie uit waarmee alle vier de waarden in een nieuwe record worden opgeslagen.

Open de getypte gegevensset en klik in de ontwerpfunctie met de rechtermuisknop op de CategoriesTableAdapter koptekst en kies Query toevoegen in het contextmenu. Hiermee opent u de tableadapter-wizard voor queryconfiguratie, die begint met het vragen hoe de TableAdapter-query toegang moet krijgen tot de database. Kies SQL-instructies gebruiken en klik op Volgende. In de volgende stap wordt gevraagd om het type query dat moet worden gegenereerd. Omdat we een query maken om een nieuwe record aan de Categories tabel toe te voegen, kiest u INSERT en klikt u op Volgende.

Selecteer de optie INVOEGEN

Afbeelding 1: Selecteer de optie INVOEGEN (klik om de afbeelding op volledige grootte weer te geven)

We moeten nu de INSERT SQL-instructie opgeven. De wizard stelt automatisch een INSERT instructie voor die overeenkomt met de hoofdquery van TableAdapter. In dit geval is het een INSERT instructie waarmee de CategoryName, Descriptionen BrochurePath waarden worden ingevoegd. Werk de instructie bij zodat de Picture kolom samen met een @Picture parameter wordt opgenomen, zoals:

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

In het laatste scherm van de wizard wordt ons gevraagd de nieuwe TableAdapter-methode een naam te geven. Voer de tekst in InsertWithPicture en klik op Voltooien.

Geef de nieuwe TableAdapter-methode InsertWithPicture een naam

Afbeelding 2: Geef de methode InsertWithPicture New TableAdapter een naam (klik om de afbeelding op volledige grootte weer te geven)

Stap 2: de bedrijfslogicalaag bijwerken

Omdat de presentatielaag alleen moet verbinden met de businesslogicalaag in plaats van deze omzeilen en rechtstreeks naar de data access-laag gaan, moeten we een BLL-methode maken die de DAL-methode aanroept die we zojuist hebben gemaakt (InsertWithPicture). Voor deze zelfstudie moet u een methode maken in de CategoriesBLL klasse genaamd InsertWithPicture die als invoer drie string's en een byte array accepteert. De string invoerparameters zijn voor de naam, beschrijving en brochurebestandspad van de categorie, terwijl de byte matrix voor de binaire inhoud van de afbeelding van de categorie is. Zoals de volgende code laat zien, roept deze BLL-methode de bijbehorende DAL-methode aan:

[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);
}

Opmerking

Zorg ervoor dat u de getypte gegevensset hebt opgeslagen voordat u de InsertWithPicture methode toevoegt aan de BLL. Omdat de CategoriesTableAdapter klassecode automatisch wordt gegenereerd op basis van de Getypte DataSet, zal de Adapter eigenschap niet op de hoogte zijn van de InsertWithPicture methode als u de wijzigingen niet eerst opslaat in de Getypte DataSet.

Stap 3: de bestaande categorieën en de bijbehorende binaire gegevens weergeven

In deze zelfstudie maken we een pagina waarmee een eindgebruiker een nieuwe categorie aan het systeem kan toevoegen, met een afbeelding en brochure voor de nieuwe categorie. In de vorige zelfstudie hebben we een GridView met een TemplateField en ImageField gebruikt om de naam, beschrijving, afbeelding en een koppeling voor elke categorie weer te geven om de brochure te downloaden. Laten we die functionaliteit voor deze zelfstudie repliceren, een pagina maken waarin alle bestaande categorieën worden vermeld en waarmee nieuwe categorieën kunnen worden gemaakt.

Open eerst de DisplayOrDownload.aspx pagina vanuit de BinaryData map. Ga naar de bronweergave en kopieer de declaratieve syntaxis van GridView en ObjectDataSource en plak deze in het <asp:Content> element in UploadInDetailsView.aspx. Vergeet ook niet om de GenerateBrochureLink methode te kopiëren van de code-behind-klasse van DisplayOrDownload.aspx naar UploadInDetailsView.aspx.

Kopieer en plak de declaratieve syntaxis van DisplayOrDownload.aspx in UploadInDetailsView.aspx

Afbeelding 3: De declaratieve syntaxis kopiëren en plakken van DisplayOrDownload.aspxUploadInDetailsView.aspx waaruit (klik om de volledige afbeelding weer te geven)

Nadat u de declaratieve syntaxis en GenerateBrochureLink methode naar de UploadInDetailsView.aspx pagina hebt gekopieerd, bekijkt u de pagina via een browser om ervoor te zorgen dat alles correct is gekopieerd. U ziet nu een GridView met de acht categorieën die een koppeling bevatten om de brochure en de afbeelding van de categorie te downloaden.

U ziet nu elke categorie samen met de binaire gegevens

Afbeelding 4: U ziet nu elke categorie samen met de binaire gegevens (klik om de volledige afbeelding weer te geven)

Stap 4: Configureren om invoegen te ondersteunenCategoriesDataSource

De CategoriesDataSource ObjectDataSource die wordt gebruikt door GridView Categories biedt momenteel niet de mogelijkheid om gegevens in te voegen. Om het invoegen via dit besturingselement voor gegevensbronnen te ondersteunen, moeten we de Insert methode toewijzen aan een methode in het onderliggende object. CategoriesBLL In het bijzonder willen we dit toewijzen aan de CategoriesBLL methode die we in stap 2 InsertWithPicturehebben toegevoegd.

Klik eerst op de koppeling Gegevensbron configureren vanuit de infotag van ObjectDataSource. In het eerste scherm ziet u het object waarmee de gegevensbron is geconfigureerd om mee te werken. CategoriesBLL Laat deze instelling as-is ongewijzigd en klik op Volgende om naar het scherm Definiëring van Gegevensmethoden te gaan. Ga naar het tabblad INSERT en kies de InsertWithPicture methode in de vervolgkeuzelijst. Klik op Voltooien om de wizard te voltooien.

De ObjectDataSource configureren voor het gebruik van de Methode InsertWithPicture

Afbeelding 5: De ObjectDataSource configureren om de InsertWithPicture methode te gebruiken (klik om de afbeelding op volledige grootte weer te geven)

Opmerking

Wanneer u de wizard hebt voltooid, kan Visual Studio u vragen of u velden en sleutels wilt vernieuwen, waarmee de velden voor gegevenswebbesturingselementen opnieuw worden gegenereerd. Kies Nee, want als u Ja kiest, worden alle veldaanpassingen die u hebt aangebracht, overschreven.

Nadat de wizard is voltooid, bevat ObjectDataSource nu een waarde voor de eigenschap InsertMethod en voor de vier categoriekolommen InsertParameters, zoals de volgende declaratieve markup illustreert.

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

Stap 5: de invoeginterface maken

Zoals eerst besproken in een overzicht van het invoegen, bijwerken en verwijderen van gegevens, biedt het besturingselement DetailsView een ingebouwde invoeginterface die kan worden gebruikt bij het werken met een besturingselement voor gegevensbronnen dat ondersteuning biedt voor invoegen. Laten we een DetailView-besturingselement toevoegen aan deze pagina boven de GridView die de invoeginterface permanent weergeeft, zodat een gebruiker snel een nieuwe categorie kan toevoegen. Wanneer u een nieuwe categorie toevoegt in de DetailsView, wordt de GridView eronder automatisch vernieuwd en wordt de nieuwe categorie weergegeven.

Begin met het slepen van een DetailsView vanuit de Werkset naar de Designer boven de GridView, waarbij u de eigenschap ID instelt op NewCategory en de waarden van de eigenschappen Height en Width wist. Bind de slimme tag van DetailsView aan de bestaande CategoriesDataSource en vink vervolgens het selectievakje 'Invoegen inschakelen' aan.

Schermopname waarin DetailsView is geopend met de eigenschap CategoryID ingesteld op NewCategory, lege eigenschapswaarden Hoogte en Breedte en het selectievakje Invoegen inschakelen ingeschakeld.

Afbeelding 6: Koppel de DetailsView aan het CategoriesDataSource en inschakelen van invoegen (klik om de volledige afbeelding weer te geven)

Als u de DetailsView permanent wilt weergeven in de invoeginterface, stelt u de DefaultMode eigenschap in op Insert.

De DetailsView heeft vijf BoundFields: CategoryID, CategoryName, Description, NumberOfProducts en BrochurePath, hoewel het CategoryID BoundField niet wordt weergegeven in de invoeginterface omdat de eigenschap InsertVisible is ingesteld op false. Deze BoundFields bestaan omdat dit de kolommen zijn die worden geretourneerd door de GetCategories() methode. Dit is wat de ObjectDataSource aanroept om de gegevens op te halen. Voor het invoegen willen we echter niet toestaan dat de gebruiker een waarde voor NumberOfProducts opgeeft. Bovendien moeten we hen toestaan om een afbeelding voor de nieuwe categorie te uploaden en een PDF-bestand voor de brochure te uploaden.

Verwijder het NumberOfProducts BoundField helemaal uit de DetailsView en werk vervolgens respectievelijk de HeaderText eigenschappen van de CategoryName en BrochurePath BoundFields bij naar Categorie en Brochure. Converteer vervolgens het BrochurePath BoundField naar een TemplateField en voeg een nieuw TemplateField toe voor de afbeelding, waardoor dit nieuwe TemplateField een HeaderText waarde van Afbeelding krijgt. Verplaats het Picture TemplateField zo dat deze zich bevindt tussen het BrochurePath TemplateField en CommandField.

Schermopname van het veldenvenster met TemplateField, Picture en HeaderText gemarkeerd.

Afbeelding 7: DetailsView aan CategoriesDataSource binden en Invoegen inschakelen

Als u het BrochurePath BoundField hebt geconverteerd naar een TemplateField via het dialoogvenster Velden bewerken, bevat het sjabloonveld een ItemTemplate, EditItemTemplateen InsertItemTemplate. Alleen de InsertItemTemplate is nodig, dus voel je vrij om de andere twee sjablonen te verwijderen. Op dit moment moet de declaratieve syntaxis van uw DetailsView er als volgt uitzien:

<asp:DetailsView ID="NewCategory" runat="server" AutoGenerateRows="False" 
    DataKeyNames="CategoryID" DataSourceID="CategoriesDataSource" 
    DefaultMode="Insert">
    <Fields>
        <asp:BoundField DataField="CategoryID" HeaderText="CategoryID" 
            InsertVisible="False" ReadOnly="True" 
            SortExpression="CategoryID" />
        <asp:BoundField DataField="CategoryName" HeaderText="Category" 
            SortExpression="CategoryName" />
        <asp:BoundField DataField="Description" HeaderText="Description" 
            SortExpression="Description" />
        <asp:TemplateField HeaderText="Brochure" SortExpression="BrochurePath">
            <InsertItemTemplate>
                <asp:TextBox ID="TextBox1" runat="server"
                    Text='<%# Bind("BrochurePath") %>'></asp:TextBox>
            </InsertItemTemplate>
        </asp:TemplateField>
        <asp:TemplateField HeaderText="Picture"></asp:TemplateField>
        <asp:CommandField ShowInsertButton="True" />
    </Fields>
</asp:DetailsView>

FileUpload-besturingselementen toevoegen voor de brochure- en afbeeldingsvelden

Momenteel bevat de BrochurePath TemplateField s InsertItemTemplate een tekstvak, terwijl het Picture TemplateField geen sjablonen bevat. We moeten deze twee TemplateField s InsertItemTemplate bijwerken om FileUpload-besturingselementen te gebruiken.

Kies in de infolabel van DetailsView de optie Sjablonen bewerken en selecteer vervolgens de BrochurePath TemplateFields InsertItemTemplate in de vervolgkeuzelijst. Verwijder het tekstvak en sleep vervolgens een Besturingselement FileUpload uit de Werkset naar de sjabloon. Stel de FileUpload-controle in ID met BrochureUpload. Voeg op dezelfde manier een FileUpload-besturingselement toe aan templatefield Picture s InsertItemTemplate. Stel dit FileUpload-besturingselement in ID naar PictureUpload.

Een Besturingselement FileUpload toevoegen aan insertItemTemplate

Afbeelding 8: Voeg een FileUpload-besturingselement toe aan de InsertItemTemplate afbeelding (klik om de volledige afbeelding weer te geven)

Nadat u deze toevoegingen hebt gemaakt, is de declaratieve syntaxis van de twee TemplateFields:

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

Wanneer een gebruiker een nieuwe categorie toevoegt, willen we ervoor zorgen dat de brochure en afbeelding van het juiste bestandstype zijn. Voor de brochure moet de gebruiker een PDF opgeven. Voor de afbeelding hebben we de gebruiker nodig om een afbeeldingsbestand te uploaden, maar staan we een afbeeldingsbestand of alleen afbeeldingsbestanden van een bepaald type toe, zoals GIF's of JPG's? Om verschillende bestandstypen toe te staan, moeten we het Categories schema uitbreiden met een kolom die het bestandstype vastlegt, zodat dit type naar de client Response.ContentType kan worden verzonden in DisplayCategoryPicture.aspx. Omdat we geen dergelijke kolom hebben, is het verstandig om gebruikers te beperken tot alleen een specifiek afbeeldingsbestandstype. De bestaande afbeeldingen van de Categories tabel zijn bitmaps, maar JPG's zijn een meer geschikte bestandsindeling voor afbeeldingen die via internet worden geleverd.

Als een gebruiker een onjuist bestandstype uploadt, moeten we de invoeging annuleren en een bericht weergeven dat het probleem aangeeft. Voeg een labelwebbesturingselement toe onder de DetailsView. Stel de ID eigenschap in op UploadWarning, wis de Text eigenschap, stel de CssClass eigenschap in op Waarschuwing, en stel de Visible en EnableViewState eigenschappen in op false. De Warning CSS-klasse wordt gedefinieerd in Styles.css en geeft de tekst weer in een groot, rood, cursief, vet lettertype.

Opmerking

In het ideale voorbeeld worden de CategoryName en Description BoundFields geconverteerd naar TemplateFields en de invoeginterfaces aangepast. De Description invoeginterface is waarschijnlijk beter geschikt met een meerrijig tekstvak als voorbeeld. En omdat de CategoryName kolom geen waarden accepteert NULL , moet er een RequiredFieldValidator worden toegevoegd om ervoor te zorgen dat de gebruiker een waarde voor de naam van de nieuwe categorie opgeeft. Deze stappen worden overgelaten als oefening aan de lezer. Raadpleeg Customizing the Data Modification Interface voor een diepgaand overzicht van het verbeteren van de interfaces voor gegevenswijziging.

Stap 6: de geüploade brochure opslaan in het bestandssysteem van de webserver

Wanneer de gebruiker de waarden voor een nieuwe categorie invoert en op de knop Invoegen klikt, vindt er een postback plaats en wordt de invoegworkflow uitgevoerd. Eerst wordt de DetailsView-gebeurtenis ItemInserting geactiveerd. Vervolgens wordt de methode ObjectDataSource Insert() aangeroepen, waardoor er een nieuwe record wordt toegevoegd aan de Categories tabel. Daarna wordt de DetailsView ItemInserted gebeurtenis geactiveerd.

Voordat de methode ObjectDataSource Insert() wordt aangeroepen, moeten we eerst controleren of de juiste bestandstypen zijn geüpload door de gebruiker en vervolgens de brochure-PDF opslaan in het bestandssysteem van de webserver. Maak een gebeurtenis-handler voor de DetailsView-gebeurtenis ItemInserting en voeg de volgende code toe:

// 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;
    }
}

De gebeurtenis-handler begint door te verwijzen naar het BrochureUpload Besturingselement FileUpload vanuit de DetailsView-sjablonen. Als er vervolgens een brochure is geüpload, wordt de extensie van het geüploade bestand onderzocht. Als de extensie niet .PDF is, wordt er een waarschuwing weergegeven, wordt de invoegbewerking geannuleerd en eindigt de uitvoering van de eventhandler.

Opmerking

Vertrouwen op de bestandsextensie van het geüploade bestand is geen betrouwbare methode om ervoor te zorgen dat het geüploade bestand een PDF-document is. De gebruiker kan een geldig PDF-document met de extensie .Brochurehebben of een niet-PDF-document hebben genomen en er een .pdf extensie aan hebben gegeven. De binaire inhoud van het bestand moet programmatisch worden onderzocht om het bestandstype nauwkeuriger te verifiëren. Dergelijke grondige benaderingen zijn echter vaak overdreven; het controleren van de extensie is voldoende voor de meeste scenario's.

Zoals besproken in de zelfstudie Bestanden uploaden, moet u ervoor zorgen dat u bestanden opslaat in het bestandssysteem, zodat de upload van een gebruiker de bestanden van een andere gebruiker niet overschrijft. Voor deze zelfstudie proberen we dezelfde naam te gebruiken als het geüploade bestand. Als er al een bestand in de ~/Brochures map met dezelfde bestandsnaam bestaat, voegen we echter een nummer aan het einde toe totdat een unieke naam wordt gevonden. Als de gebruiker bijvoorbeeld een brochurebestand met de naam Meats.pdfuploadt, maar er al een bestand met de naam Meats.pdf in de ~/Brochures map staat, wordt de naam Meats-1.pdfvan het opgeslagen bestand gewijzigd in . Als dat bestaat, proberen we Meats-2.pdf, en zo verder totdat er een unieke bestandsnaam wordt gevonden.

De volgende code gebruikt de File.Exists(path) methode om te bepalen of er al een bestand bestaat met de opgegeven bestandsnaam. Zo ja, dan blijft het nieuwe bestandsnamen voor de brochure proberen totdat er geen conflict is gevonden.

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++;
}

Zodra een geldige bestandsnaam is gevonden, moet het bestand worden opgeslagen in het bestandssysteem en moet de waarde van brochurePath``InsertParameter ObjectDataSource worden bijgewerkt, zodat deze bestandsnaam naar de database wordt geschreven. Zoals we hebben gezien in de zelfstudie Bestanden uploaden , kan het bestand worden opgeslagen met behulp van de methode FileUpload control s SaveAs(path) . Gebruik de verzameling om de ObjectDataSource-parameter brochurePath bij te werken e.Values.

// Save the file to disk and set the value of the brochurePath parameter
BrochureUpload.SaveAs(Server.MapPath(brochurePath));
e.Values["brochurePath"] = brochurePath;

Stap 7: de geüploade afbeelding opslaan in de database

Als u de geüploade afbeelding in de nieuwe Categories record wilt opslaan, moet u de geüploade binaire inhoud toewijzen aan de parameter ObjectDataSource in picture de gebeurtenis DetailsView ItemInserting . Voordat we deze opdracht maken, moeten we er echter eerst voor zorgen dat de geüploade afbeelding een JPG is en niet een ander afbeeldingstype. Net als in stap 6 gaan we de bestandsextensie van de geüploade afbeelding gebruiken om het type ervan vast te stellen.

Hoewel de Categories tabel NULL waarden voor de Picture kolom toestaat, hebben alle categorieën momenteel een afbeelding. Laten we afdwingen dat de gebruiker een afbeelding opgeeft bij het toevoegen van een nieuwe categorie via deze pagina. Met de volgende code wordt gecontroleerd of een afbeelding is geüpload en of deze een geschikte extensie heeft.

// 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;
}

Deze code moet vóór de code van stap 6 worden geplaatst, zodat als er een probleem is met het uploaden van afbeeldingen, de gebeurtenis-handler wordt beëindigd voordat het brochurebestand wordt opgeslagen in het bestandssysteem.

Ervan uitgaande dat een geschikt bestand is geüpload, wijst u de geüploade binaire inhoud toe aan de waarde van de afbeeldingsparameter met de volgende coderegel:

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

De volledigeItemInsertinggebeurtenis-handler

Voor volledigheid is dit de ItemInserting gebeurtenis-handler in zijn geheel:

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;
    }
}

Stap 8: DeDisplayCategoryPicture.aspxpagina corrigeren

Laten we even de tijd nemen om de invoeginterface en ItemInserting gebeurtenis-handler te testen die in de afgelopen stappen zijn gemaakt. Ga naar de UploadInDetailsView.aspx pagina via een browser en probeer een categorie toe te voegen, maar laat de afbeelding weg of geef een niet-JPG-afbeelding of een niet-PDF-brochure op. In alle van deze gevallen wordt een foutmelding weergegeven en wordt de invoegwerkstroom geannuleerd.

Er wordt een waarschuwingsbericht weergegeven als er een ongeldig bestandstype wordt geüpload

Afbeelding 9: Er wordt een waarschuwingsbericht weergegeven als er een ongeldig bestandstype is geüpload (klik hier om de afbeelding op volledige grootte weer te geven)

Zodra u hebt geverifieerd dat op de pagina een afbeelding moet worden geüpload en niet-PDF- of NIET-JPG-bestanden worden geaccepteerd, voegt u een nieuwe categorie toe met een geldige JPG-afbeelding, waardoor het veld Brochure leeg blijft. Nadat u op de knop Invoegen hebt geklikt, wordt de pagina teruggezet en wordt er een nieuwe record toegevoegd aan de Categories tabel met de binaire inhoud van de geüploade afbeelding die rechtstreeks in de database is opgeslagen. De GridView wordt bijgewerkt en toont een rij voor de zojuist toegevoegde categorie, maar zoals in afbeelding 10 wordt weergegeven, wordt de afbeelding van de nieuwe categorie niet correct weergegeven.

De afbeelding van de nieuwe categorie wordt niet weergegeven

Afbeelding 10: De afbeelding van de nieuwe categorie wordt niet weergegeven (klik om de afbeelding op volledige grootte weer te geven)

De reden waarom de nieuwe afbeelding niet wordt weergegeven, is omdat de DisplayCategoryPicture.aspx pagina die een opgegeven categorieafbeelding retourneert, is geconfigureerd voor het verwerken van bitmaps met een OLE-header. Deze koptekst van 78 byte wordt verwijderd uit de binaire inhoud van de Picture kolom voordat deze naar de client worden verzonden. Maar het JPG-bestand dat we zojuist hebben geüpload voor de nieuwe categorie heeft deze OLE-header niet; daarom worden geldige, benodigde bytes verwijderd uit de binaire gegevens van de afbeelding.

Omdat er nu zowel bitmaps met OLE-headers als JPG's in de Categories tabel staan, moeten we bijwerken DisplayCategoryPicture.aspx zodat de OLE-header wordt verwijderd voor de oorspronkelijke acht categorieën en deze stripping voor de nieuwere categorierecords wordt overgeslagen. In onze volgende zelfstudie bekijken we hoe u een bestaande recordafbeelding bijwerkt en alle oude categorieafbeeldingen bijwerkt, zodat ze JPG's zijn. Voorlopig gebruikt u de volgende code in DisplayCategoryPicture.aspx om de OLE-headers alleen te verwijderen voor de oorspronkelijke acht categorieën:

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);
    }
}

Met deze wijziging wordt de JPG-afbeelding nu correct weergegeven in de GridView.

De JPG-afbeeldingen voor nieuwe categorieën worden correct weergegeven

Afbeelding 11: De JPG-afbeeldingen voor nieuwe categorieën worden correct weergegeven (klik om de afbeelding volledig weer te geven)

Stap 9: De brochure verwijderen bij een uitzondering

Een van de uitdagingen van het opslaan van binaire gegevens op het bestandssysteem van de webserver is dat er een breuk ontstaat tussen het gegevensmodel en de bijbehorende binaire gegevens. Wanneer een record wordt verwijderd, moeten de bijbehorende binaire gegevens in het bestandssysteem daarom ook worden verwijderd. Dit kan ook in het spel komen bij het invoegen. Overweeg het volgende scenario: een gebruiker voegt een nieuwe categorie toe, waarbij een geldige afbeelding en brochure wordt opgegeven. Wanneer u op de knop Invoegen klikt, treedt er een postback op en wordt de DetailsView ItemInserting-gebeurtenis geactiveerd, waarna de brochure wordt opgeslagen in het bestandssysteem van de webserver. Vervolgens wordt de Insert() methode van de ObjectDataSource aangeroepen, die de CategoriesBLL methode van de InsertWithPicture klasse aanroept, waarmee de CategoriesTableAdapter methode van de InsertWithPicture wordt aangeroepen.

Wat gebeurt er als de database offline is of als er een fout optreedt in de INSERT SQL-instructie? De INSERT mislukt duidelijk, dus er wordt geen nieuwe categorierij toegevoegd aan de database. Maar we hebben nog steeds het geüploade brochurebestand op het bestandssysteem van de webserver! Dit bestand moet worden verwijderd bij een uitzondering tijdens het invoegproces in de werkstroom.

Zoals eerder besproken in de afhandelen van BLL- en DAL-Level Uitzonderingen in een ASP.NET pagina zelfstudie, wanneer een uitzondering vanuit de diepten van de architectuur wordt gegenereerd, wordt deze door de verschillende lagen heen naar boven geleid. Op de presentatielaag kunnen we vaststellen of er een uitzondering is opgetreden vanuit de gebeurtenis van de DetailsView ItemInserted. Deze gebeurtenis-handler biedt ook de waarden van de ObjectDataSource s InsertParameters. Daarom kunnen we een gebeurtenis-handler maken voor de ItemInserted gebeurtenis die controleert of er een uitzondering is en, als dat het geval is, het bestand verwijdert dat is opgegeven door de parameter ObjectDataSource 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()));
    }
}

Samenvatting

Er zijn een aantal stappen die moeten worden uitgevoerd om een webinterface te bieden voor het toevoegen van records die binaire gegevens bevatten. Als de binaire gegevens rechtstreeks in de database worden opgeslagen, moet u de architectuur bijwerken en specifieke methoden toevoegen om te kunnen omgaan met het geval waarin binaire gegevens worden ingevoegd. Zodra de architectuur is bijgewerkt, maakt de volgende stap de invoeginterface, die kan worden uitgevoerd met behulp van een DetailsView die is aangepast om een FileUpload-besturingselement voor elk binair gegevensveld op te nemen. De geüploade gegevens kunnen vervolgens worden opgeslagen in het bestandssysteem van de webserver of worden toegewezen aan een gegevensbronparameter in de gebeurtenis-handler van ItemInserting DetailsView.

Het opslaan van binaire gegevens in het bestandssysteem vereist meer planning dan het rechtstreeks opslaan van gegevens in de database. Er moet een naamgevingsschema worden gekozen om te voorkomen dat een gebruiker een andere upload overschrijft. Er moeten ook extra stappen worden ondernomen om het geüploade bestand te verwijderen als het invoegen van de database mislukt.

We hebben nu de mogelijkheid om nieuwe categorieën toe te voegen aan het systeem met een brochure en afbeelding, maar we moeten nog kijken hoe u de binaire gegevens van een bestaande categorie bijwerkt of hoe u de binaire gegevens voor een verwijderde categorie correct verwijdert. In de volgende zelfstudie gaan we deze twee onderwerpen verkennen.

Veel plezier met programmeren!

Over de auteur

Scott Mitchell, auteur van zeven ASP/ASP.NET-boeken en oprichter van 4GuysFromRolla.com, werkt sinds 1998 met Microsoft-webtechnologieën. Scott werkt als onafhankelijk consultant, trainer en schrijver. Zijn laatste boek is Sams Teach Yourself ASP.NET 2.0 in 24 uur. Hij kan worden bereikt op mitchell@4GuysFromRolla.com.

Speciale dank aan

Deze tutorialreeks is beoordeeld door veel behulpzame beoordelers. Hoofdrecensenten voor deze zelfstudie waren Dave Gardner, Teresa Murphy en Bernadette Leigh. Bent u geïnteresseerd in het bekijken van mijn aanstaande MSDN-artikelen? Zo ja, laat iets van je horen via mitchell@4GuysFromRolla.com.