Inclusione di un'opzione per il caricamento di file durante l'aggiunta di un nuovo record (C#)
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 CategoryName
campi , Description
e 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 unInsertWithPicture
metodo aCategoriesTableAdapter
Quando è stato creato di CategoriesTableAdapter
nuovo nell'esercitazione Creazione di un livello di accesso ai dati , è stato configurato per generare INSERT
automaticamente istruzioni , UPDATE
e DELETE
in base alla query principale. Inoltre, è stato richiesto all'oggetto TableAdapter di usare l'approccio db Direct, che ha creato i metodi Insert
, Update
e Delete
. Questi metodi eseguono le istruzioni , UPDATE
e DELETE
generate INSERT
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 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 CategoryName
colonne , Description
, BrochurePath
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, 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.
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 CategoryName
valori , Description
e 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.
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
.
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.
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.
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.
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
, , Description
CategoryName
, NumberOfProducts
e 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.
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 ItemTemplate
oggetto , 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 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 ID
BrochureUpload
. Analogamente, aggiungere un controllo FileUpload a Picture
TemplateField s InsertItemTemplate
. Impostare questo controllo FileUpload su ID
PictureUpload
.
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à ID
UploadWarning
su , 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 CategoryName
Description
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 .Brochure
o 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 ~/Brochures
Meats.pdf
, il nome del file salvato verrà modificato in Meats-1.pdf
. In tal caso, si proverà Meats-2.pdf
e 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.aspx
pagina
È 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.
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.
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.
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.