Condividi tramite


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

di Scott Mitchell

Scaricare 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, come usare il controllo FileUpload per inviare file dal client al server Web e come presentare questi dati binari in un controllo Web dati. Non abbiamo ancora parlato di come associare i dati caricati al modello dei 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 l'immagine della nuova 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 query principale CategoriesTableAdapter non recupera la colonna Picture. Di conseguenza, il metodo generato automaticamente Insert include solo input per i CategoryNamecampi , Descriptione BrochurePath . È quindi necessario creare un metodo aggiuntivo nel TableAdapter che richiede tutti e quattro Categories i campi. Anche la CategoriesBLL classe nel livello della logica di business dovrà essere aggiornata.

Passaggio 1: Aggiungere unInsertWithPicturemetodo alCategoriesTableAdapter

Abbiamo creato CategoriesTableAdapter nel tutorial Creazione di un livello di accesso ai dati, configurandolo per generare automaticamente istruzioni INSERT, UPDATE e DELETE basate sulla query principale. È stato inoltre indicato a TableAdapter di usare l'approccio DB Direct, che ha creato i metodi Insert, Updatee Delete. Questi metodi eseguono le istruzioni INSERT, UPDATE e DELETE generate automaticamente 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 con la personalizzazione dell'istruzione generata automaticamente INSERT è che rischiamo che le nostre personalizzazioni vengano sovrascritte dalla 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à Insert aggiornato 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 funziona 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.

Annotazioni

Questo fastidio non si verifica 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 , DescriptionBrochurePath, e Picture ed eseguirà un'istruzione INSERT che archivia tutti e quattro i valori in un nuovo record.

Aprire il set di dati tipizzato e, in Progettazione, fare clic con il pulsante destro del mouse sull'intestazione CategoriesTableAdapter s e scegliere Aggiungi query dal menu di scelta rapida. Si apre la Configurazione guidata della query di TableAdapter, che inizia chiedendoci in che modo la query di TableAdapter deve accedere al database. Scegliere Usa istruzioni SQL e fare clic su Avanti. Il passaggio successivo richiede la generazione del tipo di query. 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, si tratta di 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 di 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 e una Byte matrice. I String parametri di input sono relativi al nome, alla descrizione e al percorso del file della 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 Sub InsertWithPicture(categoryName As String, description As String, _
    brochurePath As String, picture() As Byte)
    
    Adapter.InsertWithPicture(categoryName, description, brochurePath, picture)
End Sub

Annotazioni

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 all'oggetto DataSet tipizzato, se non si salvano prima le modifiche nel set di dati 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 oggetto TemplateField e ImageField 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 di 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 UploadInDetailsView.aspx metodo nella 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.

A questo ora dovrebbe essere visualizzata ogni categoria insieme ai relativi dati binari

Figura 4: Dovrebbe essere visualizzata ogni categoria insieme ai relativi dati binari (fare clic per visualizzare l'immagine a dimensione intera)

Passaggio 4: Configurazione di CategoriesDataSource per supportare l'inserimento

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

Per iniziare, fare clic sul collegamento Configura origine dati dallo smart tag ObjectDataSource s. La prima schermata mostra l'oggetto con cui l'origine dati è configurata, CategoriesBLL. Lasciare questa impostazione as-is 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 metodo (InsertWithPicture a dimensione intera)

Annotazioni

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é scegliendo Sì si sovrascriveranno le personalizzazioni dei campi che potrebbero essere state apportate.

Dopo aver completato la 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 in una 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 utilizza un controllo origine dati che supporta l'inserimento. Aggiungiamo un controllo DetailsView a questa pagina sopra il GridView, che renderizzerà permanentemente la sua interfaccia di inserimento, consentendo a un utente di aggiungere rapidamente una nuova categoria. Dopo aver aggiunto una nuova categoria in DetailsView, GridView sotto di esso verrà automaticamente aggiornata e visualizzerà la nuova categoria.

Per iniziare, trascinare un controllo DetailsView dalla casella degli strumenti nella finestra di progettazione 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 di DetailsView con la proprietà CategoryID impostata su NewCategory, i valori delle proprietà Height e Width sono 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, CategoryName, Description, 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 , ovvero 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 BoundField NumberOfProducts da DetailsView e quindi aggiornare le HeaderText proprietà di CategoryName e BrochurePath BoundFields rispettivamente a Category e Brochure. Successivamente, convertire BoundField BrochurePath in un oggetto TemplateField e aggiungere un nuovo TemplateField per l'immagine, assegnando a questo nuovo TemplateField il HeaderText valore Picture. Spostare TemplateField Picture in modo che sia compreso tra BrochurePath TemplateField e CommandField.

Screenshot che mostra la finestra dei campi con TemplateField, Picture e HeaderText evidenziati.

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

Se l'oggetto BrochurePath BoundField è stato convertito in un oggetto TemplateField tramite la finestra di dialogo Modifica campi, TemplateField include un ItemTemplate, EditItemTemplate e 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 dei controlli di caricamento file per i campi Brochure e Immagine

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

Dallo smart tag di DetailsView, scegliere l'opzione Modifica Modelli e quindi selezionare BrochurePath TemplateField dal menu a tendina InsertItemTemplate. Rimuovere il controllo TextBox e quindi trascinare un controllo FileUpload dalla casella degli strumenti nel modello. Imposta il controllo FileUpload su IDBrochureUpload. Analogamente, aggiungere un controllo FileUpload a Picture TemplateField s InsertItemTemplate. Impostare il controllo FileUpload su ID a PictureUpload.

Aggiungere il controllo FileUpload nell'InsertItemTemplate

Figura 8: Aggiungere un controllo FileUpload al InsertItemTemplate (fare clic per visualizzare l'immagine 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 è 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 Etichetta Web sotto il DetailsView. Impostare la proprietà ID a UploadWarning, cancellare la proprietà Text, impostare la proprietà CssClass a Avviso, e le proprietà Visible e EnableViewState a False. La Warning classe CSS è definita in Styles.css ed esegue il rendering del testo in un carattere grande, rosso, corsivo, grassetto.

Annotazioni

Idealmente, e CategoryNameDescription BoundFields verrebbero convertiti in TemplateFields e le relative interfacce di inserimento personalizzate. L'interfaccia Description di inserimento, ad esempio, sarebbe probabilmente più adatta tramite una casella di testo a più righe. Inoltre, poiché la CategoryName colonna non accetta NULL valori, deve essere aggiunto un RequiredFieldValidator per assicurarsi che l'utente fornisca un valore per il nome della nuova categoria. Questi passaggi vengono lasciati come esercizio al 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'ItemInserting evento di DetailsView. Viene quindi richiamato il metodo s Insert() ObjectDataSource, che comporta l'aggiunta di un nuovo record alla Categories tabella. Successivamente, l'evento ItemInserted di DetailsView viene generato.

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 ItemInserting e aggiungere il codice seguente:

' Reference the FileUpload controls
Dim BrochureUpload As FileUpload = _
    CType(NewCategory.FindControl("BrochureUpload"), FileUpload)
If BrochureUpload.HasFile Then
    ' Make sure that a PDF has been uploaded
    If String.Compare(System.IO.Path.GetExtension _
        (BrochureUpload.FileName), ".pdf", True) <> 0 Then
        UploadWarning.Text = _
            "Only PDF documents may be used for a category's brochure."
        UploadWarning.Visible = True
        e.Cancel = True
        Exit Sub
    End If
End If

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.

Annotazioni

Basarsi sull'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 preso un documento non PDF e dato 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 accurati, però, sono spesso troppo esagerati; controllare l'estensione è 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 quello di un altro. 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 esiste già un file denominato Meats.pdf nella ~/Brochures cartella , il nome del file salvato verrà modificato in Meats-1.pdf. Se esistente, si proverà Meats-2.pdfe così via fino a quando non viene trovato un nome 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 BrochureDirectory As String = "~/Brochures/"
Dim brochurePath As String = BrochureDirectory & BrochureUpload.FileName
Dim fileNameWithoutExtension As String = _
    System.IO.Path.GetFileNameWithoutExtension(BrochureUpload.FileName)
Dim iteration As Integer = 1
While System.IO.File.Exists(Server.MapPath(brochurePath))
    brochurePath = String.Concat(BrochureDirectory, _
        fileNameWithoutExtension, "-", iteration, ".pdf")
    iteration += 1
End While

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 in precedenza nell'esercitazione Caricamento di File, il file può essere salvato utilizzando il metodo del controllo FileUploadSaveAs(path). Per aggiornare il parametro di ObjectDataSource brochurePath, utilizzare la raccolta e.Values.

' 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, dobbiamo 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 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
Dim PictureUpload As FileUpload = _
    CType(NewCategory.FindControl("PictureUpload"), FileUpload)
If PictureUpload.HasFile Then
    ' Make sure that a JPG has been uploaded
    If  String.Compare(System.IO.Path.GetExtension(PictureUpload.FileName), _
            ".jpg", True) <> 0 AndAlso _
        String.Compare(System.IO.Path.GetExtension(PictureUpload.FileName), _
            ".jpeg", True) <> 0 Then
        
        UploadWarning.Text = _
            "Only JPG documents may be used for a category's picture."
        UploadWarning.Visible = True
        e.Cancel = True
        Exit Sub
    End If
Else
    ' No picture uploaded!
    UploadWarning.Text = _
        "You must provide a picture for the new category."
    UploadWarning.Visible = True
    e.Cancel = True
    Exit Sub
End If

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 del salvataggio del file brochure nel file system.

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

' Set the value of the picture parameter
e.Values("picture") = PictureUpload.FileBytes

Il gestore eventi completoItemInserting

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

Protected Sub NewCategory_ItemInserting _
    (sender As Object, e As DetailsViewInsertEventArgs) _
    Handles NewCategory.ItemInserting
    
    ' Reference the FileUpload controls
    Dim PictureUpload As FileUpload = _
        CType(NewCategory.FindControl("PictureUpload"), FileUpload)
    If PictureUpload.HasFile Then
        ' Make sure that a JPG has been uploaded
        If  String.Compare(System.IO.Path.GetExtension(PictureUpload.FileName), _
                ".jpg", True) <> 0 AndAlso _
            String.Compare(System.IO.Path.GetExtension(PictureUpload.FileName), _
                ".jpeg", True) <> 0 Then
            
            UploadWarning.Text = _
                "Only JPG documents may be used for a category's picture."
            UploadWarning.Visible = True
            e.Cancel = True
            Exit Sub
        End If
    Else
        ' No picture uploaded!
        UploadWarning.Text = _
            "You must provide a picture for the new category."
        UploadWarning.Visible = True
        e.Cancel = True
        Exit Sub
    End If
    ' Set the value of the picture parameter
    e.Values("picture") = PictureUpload.FileBytes
    ' Reference the FileUpload controls
    Dim BrochureUpload As FileUpload = _
        CType(NewCategory.FindControl("BrochureUpload"), FileUpload)
    If BrochureUpload.HasFile Then
        ' Make sure that a PDF has been uploaded
        If String.Compare(System.IO.Path.GetExtension(BrochureUpload.FileName), _
            ".pdf", True) <> 0 Then
            
            UploadWarning.Text = _
                "Only PDF documents may be used for a category's brochure."
            UploadWarning.Visible = True
            e.Cancel = True
            Exit Sub
        End If
        Const BrochureDirectory As String = "~/Brochures/"
        Dim brochurePath As String = BrochureDirectory & BrochureUpload.FileName
        Dim fileNameWithoutExtension As String = _
            System.IO.Path.GetFileNameWithoutExtension(BrochureUpload.FileName)
        Dim iteration As Integer = 1
        While System.IO.File.Exists(Server.MapPath(brochurePath))
            brochurePath = String.Concat(BrochureDirectory, _
                fileNameWithoutExtension, "-", iteration, ".pdf")
            iteration += 1
        End While
        ' Save the file to disk and set the value of the brochurePath parameter
        BrochureUpload.SaveAs(Server.MapPath(brochurePath))
        e.Values("brochurePath") = brochurePath
    End If
End Sub

Passaggio 8: Correzione dellaDisplayCategoryPicture.aspxpagina

Prendiamoci un momento per testare l'interfaccia di inserimento e il gestore di eventi ItemInserting, creati negli step precedenti. 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 caricata 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 della 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 inviati 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 da eseguire la rimozione dell'intestazione OLE per le otto categorie originali e ignorare questa rimozione per i record di categoria più recenti. Nell'esercitazione successiva esamineremo come aggiornare l'immagine di un record esistente e aggiorneremo tutte le vecchie immagini di categoria in modo che siano nel formato JPG. Per il momento, tuttavia, usare il codice seguente in DisplayCategoryPicture.aspx per rimuovere le intestazioni OLE solo per le otto categorie originali:

Protected Sub Page_Load(sender As Object, e As EventArgs) Handles Me.Load
    Dim categoryID As Integer = Convert.ToInt32(Request.QueryString("CategoryID"))
    ' Get information about the specified category
    Dim categoryAPI As New CategoriesBLL()
    Dim categories As Northwind.CategoriesDataTable = _
        categoryAPI.GetCategoryWithBinaryDataByCategoryID(categoryID)
    Dim category As Northwind.CategoriesRow = categories(0)
    If categoryID <= 8 Then
        ' Output HTTP headers providing information about the binary data
        Response.ContentType = "image/bmp"
        ' Output the binary data
        ' But first we need to strip out the OLE header
        Const OleHeaderLength As Integer = 78
        Dim strippedImageLength As Integer = _
            category.Picture.Length - OleHeaderLength
        Dim strippedImageData(strippedImageLength) As Byte
        Array.Copy(category.Picture, OleHeaderLength, _
            strippedImageData, 0, strippedImageLength)
        Response.BinaryWrite(strippedImageData)
    Else
        ' For new categories, images are JPGs...
        ' Output HTTP headers providing information about the binary data
        Response.ContentType = "image/jpeg"
        ' Output the binary data
        Response.BinaryWrite(category.Picture)
    End If
End Sub

Con questa modifica, il rendering dell'immagine JPG viene 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 faccia a un'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, si verifica un postback e viene generato l'evento ItemInserting di DetailsView, salvando la brochure nel file system del server Web. Viene quindi richiamato il metodo Insert() di ObjectDataSource, che chiama il metodo CategoriesBLL della classe InsertWithPicture, che chiama il metodo CategoriesTableAdapter di InsertWithPicture.

Cosa accade ora se il database è offline o se si verifica un errore nell'istruzione INSERT SQL? L'istruzione INSERT avrà esito negativo, pertanto non verrà aggiunta alcuna nuova riga di categoria al database. Ma abbiamo ancora il file brochure caricato che risiede nel 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 Gestione delle eccezioni BLL e DAL-Level in una pagina ASP.NET, quando viene generata un'eccezione dalle profondità dell'architettura, viene propagata attraverso 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. Pertanto, è 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 Sub NewCategory_ItemInserted _
    (sender As Object, e As DetailsViewInsertedEventArgs) _
    Handles NewCategory.ItemInserted
    
    If e.Exception IsNot Nothing Then
        ' Need to delete brochure file, if it exists
        If e.Values("brochurePath") IsNot Nothing Then
            System.IO.File.Delete(Server.MapPath _
                (e.Values("brochurePath").ToString()))
        End If
    End If
End Sub

Riassunto

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 dell'origine dati nel gestore eventi detailsView.ItemInserting

Il salvataggio dei dati binari nel file system richiede più pianificazione rispetto al salvataggio diretto dei dati nel database. È necessario scegliere uno schema di denominazione per evitare che il caricamento di un utente sovrascriva un altro. Inoltre, è necessario eseguire ulteriori passaggi per eliminare il file caricato se l'inserimento del database non riesce.

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, ha lavorato 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 a mitchell@4GuysFromRolla.com.

Grazie speciale a

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