Condividi tramite


Inclusione di un'opzione per il caricamento di file durante l'aggiunta di un nuovo record (C#)

di Scott Mitchell

Scarica il PDF

Questa esercitazione illustra come creare un'interfaccia Web che consente all'utente di immettere dati di testo e caricare file binari. Per illustrare le opzioni disponibili per archiviare i dati binari, un file verrà salvato nel database mentre l'altro viene archiviato nel file system.

Introduzione

Nelle due esercitazioni precedenti sono state esaminate le tecniche per l'archiviazione di dati binari associati al modello di dati dell'applicazione, è stato illustrato come usare il controllo FileUpload per inviare file dal client al server Web e come presentare questi dati binari in un controllo Web dati. Tuttavia, è stato illustrato come associare i dati caricati al modello di dati.

In questa esercitazione verrà creata una pagina Web per aggiungere una nuova categoria. Oltre a TextBoxes per il nome e la descrizione della categoria, questa pagina dovrà includere due controlli FileUpload uno per la nuova immagine della categoria e uno per la brochure. L'immagine caricata verrà archiviata direttamente nella colonna del Picture nuovo record, mentre la brochure verrà salvata nella ~/Brochures cartella con il percorso del file salvato nella colonna del BrochurePath nuovo record.

Prima di creare questa nuova pagina Web, è necessario aggiornare l'architettura. La CategoriesTableAdapter query principale s non recupera la Picture colonna. Di conseguenza, il metodo generato Insert automaticamente include solo input per i CategoryNamecampi , Descriptione BrochurePath . È quindi necessario creare un metodo aggiuntivo nel TableAdapter che richiede tutti e quattro Categories i campi. È necessario aggiornare anche la CategoriesBLL classe nel livello della logica di business.

Passaggio 1: Aggiunta di unInsertWithPicturemetodo aCategoriesTableAdapter

Quando è stato creato di CategoriesTableAdapter nuovo nell'esercitazione Creazione di un livello di accesso ai dati , è stato configurato per generare INSERTautomaticamente istruzioni , UPDATEe DELETE in base alla query principale. Inoltre, è stato richiesto all'oggetto TableAdapter di usare l'approccio db Direct, che ha creato i metodi Insert, Updatee Delete. Questi metodi eseguono le istruzioni , UPDATEe DELETE generate INSERTautomaticamente e, di conseguenza, accettano parametri di input in base alle colonne restituite dalla query principale. Nell'esercitazione Caricamento di file è stata aumentata la CategoriesTableAdapter query principale per usare la BrochurePath colonna .

Poiché la CategoriesTableAdapter query principale non fa riferimento alla Picture colonna, non è possibile aggiungere un nuovo record né aggiornare un record esistente con un valore per la Picture colonna. Per acquisire queste informazioni, è possibile creare un nuovo metodo nell'oggetto TableAdapter usato in modo specifico per inserire un record con dati binari oppure personalizzare l'istruzione generata automaticamente INSERT . Il problema relativo alla personalizzazione dell'istruzione generata automaticamente INSERT è che si rischia di sovrascrivere le personalizzazioni tramite la procedura guidata. Si supponga, ad esempio, di aver personalizzato l'istruzione INSERT per includere l'uso della Picture colonna. In questo modo il metodo TableAdapter verrà aggiornato Insert in modo da includere un parametro di input aggiuntivo per i dati binari dell'immagine della categoria. È quindi possibile creare un metodo nel livello della logica di business per usare questo metodo DAL e richiamare questo metodo BLL tramite il livello presentazione e tutto funzionerebbe perfettamente. Ovvero, fino alla successiva configurazione di TableAdapter tramite la Configurazione guidata TableAdapter. Non appena la procedura guidata è stata completata, le personalizzazioni dell'istruzione INSERT verranno sovrascritte, il Insert metodo verrà ripristinato al formato precedente e il codice non verrà più compilato.

Nota

Questa annotazione è un problema quando si usano stored procedure anziché istruzioni SQL ad hoc. Un'esercitazione futura esaminerà l'uso di stored procedure anziché istruzioni SQL ad hoc nel livello di accesso ai dati.

Per evitare questo potenziale mal di testa, invece di personalizzare le istruzioni SQL generate automaticamente, è possibile creare invece un nuovo metodo per TableAdapter. Questo metodo, denominato InsertWithPicture, accetterà i valori per le CategoryNamecolonne , Description, BrochurePathe Picture ed eseguirà un'istruzione INSERT che archivia tutti e quattro i valori in un nuovo record.

Aprire il set di dati tipizzato e, dal Designer, fare clic con il pulsante destro del mouse sull'intestazione CategoriesTableAdapter s e scegliere Aggiungi query dal menu di scelta rapida. Verrà avviata la Configurazione guidata query TableAdapter, che inizia chiedendo come la query TableAdapter deve accedere al database. Scegliere Usa istruzioni SQL e fare clic su Avanti. Il passaggio successivo richiede il tipo di query da generare. Poiché si sta creando una query per aggiungere un nuovo record alla Categories tabella, scegliere INSERT e fare clic su Avanti.

Selezionare l'opzione INSERT

Figura 1: Selezionare l'opzione INSERT (fare clic per visualizzare l'immagine a dimensione intera)

È ora necessario specificare l'istruzione INSERT SQL. La procedura guidata suggerisce automaticamente un'istruzione INSERT corrispondente alla query principale di TableAdapter. In questo caso, è un'istruzione INSERT che inserisce i CategoryNamevalori , Descriptione BrochurePath . Aggiornare l'istruzione in modo che la Picture colonna sia inclusa insieme a un @Picture parametro, come illustrato di seguito:

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

La schermata finale della procedura guidata chiede di assegnare un nome al nuovo metodo TableAdapter. Immettere InsertWithPicture e fare clic su Fine.

Denominare il nuovo metodo TableAdapter InsertWithPicture

Figura 2: Assegnare un nome al nuovo metodo InsertWithPicture TableAdapter (fare clic per visualizzare l'immagine a dimensione intera)

Passaggio 2: Aggiornamento del livello della logica di business

Poiché il livello presentazione deve interfacciarsi solo con il livello della logica di business anziché ignorarlo per passare direttamente al livello di accesso ai dati, è necessario creare un metodo BLL che richiama il metodo DAL appena creato (InsertWithPicture). Per questa esercitazione, creare un metodo nella CategoriesBLL classe denominata InsertWithPicture che accetta come input tre string s e una byte matrice. I string parametri di input sono relativi al nome, alla descrizione e al percorso del file brochure della categoria, mentre la byte matrice è per il contenuto binario dell'immagine della categoria. Come illustrato nel codice seguente, questo metodo BLL richiama il metodo DAL corrispondente:

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

Nota

Assicurarsi di aver salvato il set di dati tipizzato prima di aggiungere il InsertWithPicture metodo al BLL. Poiché il codice della CategoriesTableAdapter classe viene generato automaticamente in base al set di dati tipizzato, se non si salvano prima le modifiche apportate al dataset tipizzato, la Adapter proprietà non conoscerà il InsertWithPicture metodo .

Passaggio 3: Elencare le categorie esistenti e i relativi dati binari

In questa esercitazione verrà creata una pagina che consente a un utente finale di aggiungere una nuova categoria al sistema, fornendo un'immagine e una brochure per la nuova categoria. Nell'esercitazione precedente è stato usato un controllo GridView con un campo template e un campo immagine per visualizzare il nome, la descrizione, l'immagine e un collegamento per scaricare la relativa brochure. È possibile replicare tale funzionalità per questa esercitazione, creando una pagina che elenca tutte le categorie esistenti e consente la creazione di nuove categorie.

Per iniziare, aprire la DisplayOrDownload.aspx pagina dalla BinaryData cartella . Passare alla visualizzazione Origine e copiare la sintassi dichiarativa gridView e ObjectDataSource, incollandola all'interno dell'elemento <asp:Content> in UploadInDetailsView.aspx. Inoltre, non dimenticare di copiare il GenerateBrochureLink metodo dalla classe code-behind di DisplayOrDownload.aspx a UploadInDetailsView.aspx.

Copiare e incollare la sintassi dichiarativa da DisplayOrDownload.aspx a UploadInDetailsView.aspx

Figura 3: Copiare e incollare la sintassi dichiarativa da DisplayOrDownload.aspx a UploadInDetailsView.aspx (fare clic per visualizzare l'immagine a dimensione intera)

Dopo aver copiato la sintassi dichiarativa e GenerateBrochureLink il metodo nella UploadInDetailsView.aspx pagina, visualizzare la pagina tramite un browser per assicurarsi che tutto sia stato copiato correttamente. Verrà visualizzato un controllo GridView che elenca le otto categorie che includono un collegamento per scaricare la brochure e l'immagine della categoria.

Verrà ora visualizzata ogni categoria insieme ai relativi dati binari

Figura 4: È ora necessario visualizzare ogni categoria insieme ai relativi dati binari (fare clic per visualizzare l'immagine a dimensione intera)

Passaggio 4: Configurazione di per supportare l'inserimentoCategoriesDataSource

ObjectDataSource CategoriesDataSource utilizzato da Categories GridView attualmente non offre la possibilità di inserire dati. Per supportare l'inserimento tramite questo controllo origine dati, è necessario eseguire il mapping del relativo Insert metodo a un metodo nell'oggetto sottostante, CategoriesBLL. In particolare, si vuole eseguirne il CategoriesBLL mapping al metodo aggiunto di nuovo nel passaggio 2, InsertWithPicture.

Per iniziare, fare clic sul collegamento Configura origine dati dallo smart tag ObjectDataSource. La prima schermata mostra l'oggetto con cui l'origine dati è configurata per l'utilizzo, CategoriesBLL. Lasciare invariata questa impostazione e fare clic su Avanti per passare alla schermata Definisci metodi dati. Passare alla scheda INSERT e selezionare il InsertWithPicture metodo dall'elenco a discesa. Fare clic su Fine per completare la procedura guidata.

Configurare ObjectDataSource per l'utilizzo del metodo InsertWithPicture

Figura 5: Configurare ObjectDataSource per l'uso del InsertWithPicture metodo (fare clic per visualizzare l'immagine a dimensione intera)

Nota

Al termine della procedura guidata, Visual Studio potrebbe chiedere se si desidera aggiornare campi e chiavi, che rigenerano i campi dei controlli Web dati. Scegliere No, perché la scelta di Sì sovrascriverà le personalizzazioni dei campi che potrebbero essere state apportate.

Al termine della procedura guidata, ObjectDataSource includerà ora un valore per la relativa InsertMethod proprietà e InsertParameters per le quattro colonne di categoria, come illustrato nel markup dichiarativo seguente:

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

Passaggio 5: Creazione dell'interfaccia di inserimento

Come descritto per la prima volta in Panoramica dell'inserimento, dell'aggiornamento e dell'eliminazione di dati, il controllo DetailsView fornisce un'interfaccia di inserimento predefinita che può essere utilizzata quando si usa un controllo origine dati che supporta l'inserimento. Aggiungere un controllo DetailsView a questa pagina sopra GridView che eseguirà il rendering permanente dell'interfaccia di inserimento, consentendo a un utente di aggiungere rapidamente una nuova categoria. Dopo l'aggiunta di una nuova categoria in DetailsView, gridView verrà aggiornata automaticamente e visualizzata la nuova categoria.

Per iniziare, trascinare un controllo DetailsView dalla casella degli strumenti nella Designer sopra gridView, impostandone la ID proprietà NewCategory su e cancellando i valori delle Height proprietà e Width . Dallo smart tag DetailsView, associarlo all'esistente CategoriesDataSource e quindi selezionare la casella di controllo Abilita inserimento.

Screenshot che mostra DetailsView aperto con la proprietà CategoryID impostata su NewCategory, valori di proprietà Height e Width vuoti e la casella di controllo Abilita inserimento selezionata.

Figura 6: Associare DetailsView a e Abilitare l'inserimento CategoriesDataSource (fare clic per visualizzare l'immagine a dimensione intera)

Per eseguire il rendering permanente di DetailsView nell'interfaccia di inserimento, impostarne la DefaultMode proprietà su Insert.

Si noti che detailsView ha cinque BoundFields CategoryID, , DescriptionCategoryName, NumberOfProductse BrochurePath anche se il rendering di CategoryID BoundField non viene eseguito nell'interfaccia di inserimento perché la relativa InsertVisible proprietà è impostata su false. Questi BoundField esistono perché sono le colonne restituite dal GetCategories() metodo , che è ciò che ObjectDataSource richiama per recuperare i dati. Per l'inserimento, tuttavia, non si vuole consentire all'utente di specificare un valore per NumberOfProducts. Inoltre, dobbiamo consentire loro di caricare un'immagine per la nuova categoria e caricare un PDF per la brochure.

Rimuovere completamente NumberOfProducts BoundField da DetailsView e quindi aggiornare le HeaderText proprietà di CategoryName e BrochurePath BoundFields rispettivamente a Category e Brochure. Convertire quindi boundfield BrochurePath in un campo modello e aggiungere un nuovo campo modello per l'immagine, assegnando a questo nuovo templateField il HeaderText valore Picture. Spostare il Picture campo Modello in modo che sia compreso tra TemplateField BrochurePath e CommandField.

Screenshot della finestra dei campi con TemplateField, Picture e HeaderText evidenziati.

Figura 7: Associare DetailsView a e abilitare l'inserimento CategoriesDataSource

Se il BrochurePath campo bound è stato convertito in un campo modello tramite la finestra di dialogo Modifica campi, il campo modello include un ItemTemplateoggetto , EditItemTemplatee InsertItemTemplate. È tuttavia necessario solo l'oggetto InsertItemTemplate , quindi è possibile rimuovere gli altri due modelli. A questo punto la sintassi dichiarativa di DetailsView dovrebbe essere simile alla seguente:

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

Aggiunta di controlli FileUpload per i campi brochure e immagine

Attualmente, TemplateField BrochurePath contiene InsertItemTemplate un controllo TextBox, mentre templateField Picture non contiene modelli. È necessario aggiornare questi due oggetti TemplateField per InsertItemTemplate usare i controlli FileUpload.

Dallo smart tag DetailsView s scegliere l'opzione Modifica modelli e quindi selezionare TemplateField BrochurePath dall'elenco InsertItemTemplate a discesa. Rimuovere textBox e quindi trascinare un controllo FileUpload dalla casella degli strumenti nel modello. Impostare il controllo FileUpload su IDBrochureUpload. Analogamente, aggiungere un controllo FileUpload a Picture TemplateField s InsertItemTemplate. Impostare questo controllo FileUpload su IDPictureUpload.

Aggiungere un controllo FileUpload a InsertItemTemplate

Figura 8: Aggiungere un controllo FileUpload a (fare clic per visualizzare l'immagineInsertItemTemplate a dimensione intera)

Dopo aver effettuato queste aggiunte, la sintassi dichiarativa di TemplateField sarà:

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

Quando un utente aggiunge una nuova categoria, si vuole assicurarsi che la brochure e l'immagine siano del tipo di file corretto. Per la brochure, l'utente deve fornire un PDF. Per l'immagine, è necessario che l'utente carichi un file di immagine, ma sia consentito qualsiasi file di immagine o solo file di immagine di un particolare tipo, ad esempio GIF o JPG? Per consentire tipi di file diversi, è necessario estendere lo Categories schema per includere una colonna che acquisisce il tipo di file in modo che questo tipo possa essere inviato al client tramite Response.ContentType in DisplayCategoryPicture.aspx. Poiché non è disponibile una colonna di questo tipo, è consigliabile limitare gli utenti a fornire solo un tipo di file di immagine specifico. Le Categories immagini esistenti della tabella sono bitmap, ma i JPG sono un formato di file più appropriato per le immagini gestite sul Web.

Se un utente carica un tipo di file non corretto, è necessario annullare l'inserimento e visualizzare un messaggio che indica il problema. Aggiungere un controllo Web Etichetta sotto DetailsView. Impostare la proprietà IDUploadWarningsu , cancellarne Text la proprietà, impostare la CssClass proprietà su Warning e le Visible proprietà e EnableViewState su false. La Warning classe CSS è definita in Styles.css ed esegue il rendering del testo in un carattere grande, rosso, corsivo, grassetto.

Nota

Idealmente, e CategoryNameDescription BoundFields verrebbero convertiti in TemplateFields e le relative interfacce di inserimento personalizzate. L'interfaccia Description di inserimento, ad esempio, potrebbe essere più adatta tramite una casella di testo a più righe. Poiché la CategoryName colonna non accetta NULL valori, è necessario aggiungere un oggetto RequiredFieldValidator per assicurarsi che l'utente fornisca un valore per il nome della nuova categoria. Questi passaggi vengono lasciati come esercizio per il lettore. Fare riferimento a Personalizzazione dell'interfaccia di modifica dei dati per un'analisi approfondita dell'aumento delle interfacce di modifica dei dati.

Passaggio 6: Salvataggio della brochure caricata nel file system del server Web

Quando l'utente immette i valori per una nuova categoria e fa clic sul pulsante Inserisci, si verifica un postback e il flusso di lavoro di inserimento si svolge. In primo luogo, viene generato l'evento DetailsView.ItemInserting Viene quindi richiamato il metodo ObjectDataSource, Insert() che comporta l'aggiunta di un nuovo record alla Categories tabella. Successivamente, viene generato l'evento DetailsView.ItemInserted

Prima che venga richiamato il metodo ObjectDataSource, Insert() è necessario prima assicurarsi che i tipi di file appropriati siano stati caricati dall'utente e quindi salvare la brochure PDF nel file system del server Web. Creare un gestore eventi per l'evento DetailsView e ItemInserting aggiungere il codice seguente:

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

Il gestore eventi inizia facendo riferimento al BrochureUpload controllo FileUpload dai modelli di DetailsView. Quindi, se è stata caricata una brochure, viene esaminata l'estensione del file caricato. Se l'estensione non è .PDF, viene visualizzato un avviso, l'inserimento viene annullato e l'esecuzione del gestore eventi termina.

Nota

L'uso dell'estensione del file caricato non è una tecnica sicura per garantire che il file caricato sia un documento PDF. L'utente potrebbe avere un documento PDF valido con l'estensione .Brochureo potrebbe aver acquisito un documento non PDF e averla data un'estensione .pdf . Il contenuto binario del file deve essere esaminato a livello di codice per verificare in modo più definitivo il tipo di file. Questi approcci approfonditi, però, sono spesso troppo abili; verificare che l'estensione sia sufficiente per la maggior parte degli scenari.

Come illustrato nell'esercitazione Caricamento di file , è necessario prestare attenzione quando si salvano i file nel file system in modo che il caricamento di un utente non sovrascriva un altro s. Per questa esercitazione si tenterà di usare lo stesso nome del file caricato. Se nella directory esiste già un file ~/Brochures con lo stesso nome di file, tuttavia, si aggiungerà un numero alla fine fino a quando non viene trovato un nome univoco. Ad esempio, se l'utente carica un file brochure denominato Meats.pdf, ma nella cartella è già presente un file denominato ~/BrochuresMeats.pdf , il nome del file salvato verrà modificato in Meats-1.pdf. In tal caso, si proverà Meats-2.pdfe così via, fino a quando non viene trovato un nome di file univoco.

Il codice seguente usa il File.Exists(path) metodo per determinare se esiste già un file con il nome file specificato. In tal caso, continua a provare nuovi nomi di file per la brochure fino a quando non viene trovato alcun conflitto.

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

Dopo aver trovato un nome di file valido, è necessario salvare il file nel file system e il valore di ObjectDataSource deve brochurePath``InsertParameter essere aggiornato in modo che questo nome file venga scritto nel database. Come abbiamo visto nell'esercitazione Caricamento di file , il file può essere salvato usando il metodo del SaveAs(path) controllo FileUpload. Per aggiornare il parametro ObjectDataSource, brochurePath utilizzare la e.Values raccolta .

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

Passaggio 7: Salvataggio dell'immagine caricata nel database

Per archiviare l'immagine caricata nel nuovo Categories record, è necessario assegnare il contenuto binario caricato al parametro ObjectDataSource s picture nell'evento DetailsView.ItemInserting Prima di effettuare questa assegnazione, tuttavia, è necessario prima assicurarsi che l'immagine caricata sia un JPG e non un altro tipo di immagine. Come nel passaggio 6, è possibile usare l'estensione del file immagine caricata per verificarne il tipo.

Mentre la Categories tabella consente NULL i valori per la Picture colonna, tutte le categorie hanno attualmente un'immagine. Forzare l'utente a fornire un'immagine quando si aggiunge una nuova categoria tramite questa pagina. Il codice seguente verifica che un'immagine sia stata caricata e che abbia un'estensione appropriata.

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

Questo codice deve essere inserito prima del codice del passaggio 6 in modo che, in caso di problemi con il caricamento dell'immagine, il gestore eventi terminerà prima che il file della brochure venga salvato nel file system.

Supponendo che sia stato caricato un file appropriato, assegnare il contenuto binario caricato al valore del parametro immagine con la riga di codice seguente:

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

Gestore dell'evento completoItemInserting

Per completezza, ecco il ItemInserting gestore eventi nella sua interezza:

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

Passaggio 8: Correzione dellaDisplayCategoryPicture.aspxpagina

È possibile testare l'interfaccia di inserimento e ItemInserting il gestore eventi creati negli ultimi passaggi. Visitare la UploadInDetailsView.aspx pagina tramite un browser e tentare di aggiungere una categoria, ma omettere l'immagine o specificare un'immagine non JPG o una brochure non PDF. In uno di questi casi, verrà visualizzato un messaggio di errore e il flusso di lavoro di inserimento annullato.

Viene visualizzato un messaggio di avviso se viene caricato un tipo di file non valido

Figura 9: Viene visualizzato un messaggio di avviso se viene caricato un tipo di file non valido (fare clic per visualizzare l'immagine a dimensione intera)

Dopo aver verificato che la pagina richiede il caricamento di un'immagine e non accetterà file non PDF o non JPG, aggiungere una nuova categoria con un'immagine JPG valida, lasciando vuoto il campo Brochure. Dopo aver fatto clic sul pulsante Inserisci, la pagina eseguirà il postback e alla tabella verrà aggiunto Categories un nuovo record con il contenuto binario dell'immagine caricato archiviato direttamente nel database. GridView viene aggiornato e mostra una riga per la categoria appena aggiunta, ma, come illustrato nella figura 10, il rendering dell'immagine della nuova categoria non viene eseguito correttamente.

L'immagine della nuova categoria non è visualizzata

Figura 10: L'immagine della nuova categoria non è visualizzata (fare clic per visualizzare l'immagine a dimensione intera)

Il motivo per cui la nuova immagine non viene visualizzata è perché la DisplayCategoryPicture.aspx pagina che restituisce un'immagine di categoria specificata è configurata per elaborare bitmap con un'intestazione OLE. Questa intestazione di 78 byte viene rimossa dal contenuto binario della Picture colonna prima che vengano restituite al client. Ma il file JPG appena caricato per la nuova categoria non ha questa intestazione OLE; pertanto, i byte validi e necessari vengono rimossi dai dati binari dell'immagine.

Poiché nella tabella sono ora presenti sia bitmap con intestazioni OLE che JPG Categories , è necessario aggiornare DisplayCategoryPicture.aspx in modo che esecchi la rimozione dell'intestazione OLE per le otto categorie originali e ignori questa rimozione per i record di categoria più recenti. Nell'esercitazione successiva verrà illustrato come aggiornare un'immagine del record esistente e verranno aggiornate tutte le immagini di categoria precedenti in modo che siano JPG. Per il momento, tuttavia, usare il codice seguente in DisplayCategoryPicture.aspx per rimuovere le intestazioni OLE solo per le otto categorie originali:

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

Con questa modifica, il rendering dell'immagine JPG viene ora eseguito correttamente in GridView.

Il rendering delle immagini JPG per le nuove categorie viene eseguito correttamente

Figura 11: Il rendering delle immagini JPG per le nuove categorie viene eseguito correttamente (fare clic per visualizzare l'immagine a dimensione intera)

Passaggio 9: Eliminazione della brochure in caso di eccezione

Uno dei problemi di archiviazione dei dati binari nel file system del server Web consiste nel fatto che introduce una disconnessione tra il modello di dati e i relativi dati binari. Pertanto, ogni volta che viene eliminato un record, è necessario rimuovere anche i dati binari corrispondenti nel file system. Questo può entrare in gioco anche durante l'inserimento. Si consideri lo scenario seguente: un utente aggiunge una nuova categoria, specificando un'immagine e una brochure validi. Quando si fa clic sul pulsante Inserisci, viene generato un postback e viene generato l'evento DetailsView, ItemInserting salvando la brochure nel file system del server Web. Viene quindi richiamato il metodo objectDataSource, Insert() che chiama il CategoriesBLL metodo della InsertWithPicture classe , che chiama il CategoriesTableAdapter metodo s InsertWithPicture .

Cosa accade ora se il database è offline o se si verifica un errore nell'istruzione INSERT SQL? Chiaramente l'istruzione INSERT avrà esito negativo, pertanto non verrà aggiunta alcuna nuova riga di categoria al database. Ma abbiamo ancora il file brochure caricato seduto sul file system del server Web! Questo file deve essere eliminato in caso di eccezione durante il flusso di lavoro di inserimento.

Come illustrato in precedenza nell'esercitazione Sulla gestione di BLL- e DAL-Level eccezioni in un'esercitazione sulla pagina di ASP.NET , quando viene generata un'eccezione dalle profondità dell'architettura, viene inserita tra i vari livelli. A livello di presentazione è possibile determinare se si è verificata un'eccezione dall'evento DetailsView.ItemInserted Questo gestore eventi fornisce anche i valori di ObjectDataSource s InsertParameters. È quindi possibile creare un gestore eventi per l'evento ItemInserted che controlla se si è verificata un'eccezione e, in tal caso, elimina il file specificato dal parametro ObjectDataSource s 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()));
    }
}

Riepilogo

Esistono diversi passaggi da eseguire per fornire un'interfaccia basata sul Web per l'aggiunta di record che includono dati binari. Se i dati binari vengono archiviati direttamente nel database, è probabile che sia necessario aggiornare l'architettura, aggiungendo metodi specifici per gestire il caso in cui vengono inseriti dati binari. Dopo aver aggiornato l'architettura, il passaggio successivo consiste nel creare l'interfaccia di inserimento, che può essere eseguita usando un controllo DetailsView personalizzato per includere un controllo FileUpload per ogni campo dati binario. I dati caricati possono quindi essere salvati nel file system del server Web o assegnati a un parametro di origine dati nel gestore eventi detailsView.ItemInserting

Il salvataggio dei dati binari nel file system richiede una pianificazione maggiore rispetto al salvataggio dei dati direttamente nel database. È necessario scegliere uno schema di denominazione per evitare il caricamento di un utente sovrascrivendo un altro. Inoltre, è necessario eseguire ulteriori passaggi per eliminare il file caricato se l'inserimento del database ha esito negativo.

Ora abbiamo la possibilità di aggiungere nuove categorie al sistema con una brochure e un'immagine, ma abbiamo ancora esaminato come aggiornare i dati binari di una categoria esistente o come rimuovere correttamente i dati binari per una categoria eliminata. Questi due argomenti verranno esaminati nell'esercitazione successiva.

Buon programmatori!

Informazioni sull'autore

Scott Mitchell, autore di sette libri ASP/ASP.NET e fondatore di 4GuysFromRolla.com, lavora con le tecnologie Web Microsoft dal 1998. Scott lavora come consulente indipendente, formatore e scrittore. Il suo ultimo libro è Sams Teach Yourself ASP.NET 2.0 in 24 ore. Può essere raggiunto all'indirizzo mitchell@4GuysFromRolla.com. o tramite il suo blog, disponibile all'indirizzo http://ScottOnWriting.NET.

Grazie speciale a

Questa serie di esercitazioni è stata esaminata da molti revisori utili. I revisori principali di questa esercitazione erano Dave Gardner, Teresa Murphy e Bernadette Leigh. Si è interessati a esaminare i prossimi articoli MSDN? In tal caso, rilasciami una riga in mitchell@4GuysFromRolla.com.