Note
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier les répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de changer de répertoire.
par Scott Mitchell
Ce tutoriel montre comment créer une interface web qui permet à l’utilisateur d’entrer des données texte et de charger des fichiers binaires. Pour illustrer les options disponibles pour stocker des données binaires, un fichier est enregistré dans la base de données tandis que l’autre est stocké dans le système de fichiers.
Présentation
Dans les deux didacticiels précédents, nous avons exploré les techniques de stockage des données binaires associées au modèle de données de l’application, examiné comment utiliser le contrôle FileUpload pour envoyer des fichiers du client au serveur web et découvrir comment présenter ces données binaires dans un contrôle Web de données. Nous n’avons pas encore parlé de la manière d'associer les données importées au modèle de données.
Dans ce tutoriel, nous allons créer une page web pour ajouter une nouvelle catégorie. En plus des zones de texte pour le nom et la description de la catégorie, cette page doit inclure deux contrôles FileUpload un pour la nouvelle image de catégorie et l’autre pour la brochure. L’image chargée sera stockée directement dans la nouvelle colonne d’enregistrement Picture , tandis que la brochure sera enregistrée dans le ~/Brochures dossier avec le chemin d’accès au fichier enregistré dans la nouvelle colonne d’enregistrement BrochurePath .
Avant de créer cette nouvelle page web, nous devons mettre à jour l’architecture. La CategoriesTableAdapter requête principale ne récupère pas la Picture colonne. Par conséquent, la méthode générée automatiquement Insert n'a des entrées que pour les champs CategoryName, Description et BrochurePath. Par conséquent, nous devons créer une méthode supplémentaire dans TableAdapter qui invite les quatre Categories champs. La CategoriesBLL classe de la couche logique métier doit également être mise à jour.
Étape 1 : Ajout d’uneInsertWithPictureméthode auCategoriesTableAdapter
Lorsque nous avons créé le CategoriesTableAdapter précédent dans le didacticiel Création d’une couche d’accès aux données , nous l’avons configuré pour générer INSERTautomatiquement, UPDATEet DELETE des instructions basées sur la requête principale. En outre, nous avons demandé à TableAdapter d’utiliser l’approche DB Direct, qui a créé les méthodes Insert, Updateet Delete. Ces méthodes exécutent les instructions générées automatiquement INSERT, UPDATE, et DELETE, et acceptent par conséquent les paramètres d’entrée basés sur les colonnes retournées par la requête principale. Dans le didacticiel Chargement de fichiers , nous avons augmenté la CategoriesTableAdapter requête principale de l’application pour utiliser la BrochurePath colonne.
Étant donné que la requête principale CategoriesTableAdapter ne fait pas référence à la colonne Picture, nous ne pouvons ni ajouter un nouvel enregistrement ni mettre à jour un enregistrement existant avec une valeur pour la colonne Picture. Pour capturer ces informations, nous pouvons créer une nouvelle méthode dans TableAdapter qui est utilisée spécifiquement pour insérer un enregistrement avec des données binaires ou personnaliser l’instruction générée automatiquement INSERT . Le problème lié à la personnalisation de l’instruction autogénérée INSERT est que nous risquons d’avoir nos personnalisations remplacées par l’assistant. Par exemple, imaginez que nous avons personnalisé l’instruction INSERT pour inclure l’utilisation de la Picture colonne. Cela mettrait à jour la méthode TableAdapter Insert pour inclure un paramètre d’entrée supplémentaire pour les données binaires de l’image de catégorie. Nous pourrions ensuite créer une méthode dans la couche logique métier pour utiliser cette méthode DAL et appeler cette méthode BLL par le biais de la couche présentation, et tout fonctionnerait merveilleusement. Autrement dit, jusqu'à la prochaine fois où nous configurerons le TableAdapter via l’Assistant Configuration de TableAdapter. Dès que l'Assistant sera terminé, nos personnalisations à l'instruction INSERT seront écrasées, la méthode Insert reviendra à sa forme ancienne et notre code ne sera plus compilé !
Remarque
Cette gêne est un non-problème lors de l’utilisation de procédures stockées au lieu d’instructions SQL ad hoc. Un prochain tutoriel explorera l’utilisation de procédures stockées au lieu d’instructions SQL ad hoc dans la couche d’accès aux données.
Pour éviter ce mal de tête potentiel, plutôt que de personnaliser les instructions SQL générées automatiquement, nous allons créer une nouvelle méthode pour TableAdapter. Cette méthode, nommée InsertWithPicture, accepte les valeurs pour les CategoryNameDescriptionBrochurePathcolonnes , et Picture exécute une INSERT instruction qui stocke les quatre valeurs dans un nouvel enregistrement.
Ouvrez le DataSet typé et, dans le Concepteur, cliquez avec le bouton droit sur l'en-tête CategoriesTableAdapter, puis choisissez Ajouter une requête dans le menu contextuel. Cette opération lance l’Assistant Configuration des requêtes TableAdapter, qui commence par nous demander comment la requête TableAdapter doit accéder à la base de données. Choisissez Utiliser des instructions SQL, puis cliquez sur Suivant. L'étape suivante demande de préciser le type de requête à générer. Dans la mesure où nous créons une requête pour ajouter un nouvel enregistrement à la Categories table, choisissez INSERT, puis cliquez sur Suivant.
Figure 1 : Sélectionner l’option INSERT (Cliquez pour afficher l’image de taille complète)
Nous devons maintenant spécifier l’instruction INSERT SQL. L’Assistant suggère automatiquement une INSERT instruction correspondant à la requête principale du TableAdapter. Dans ce cas, il s’agit d’une INSERT instruction qui insère les valeurs CategoryName, Description, et BrochurePath. Mettez à jour l’instruction afin que la Picture colonne soit incluse avec un @Picture paramètre, comme suit :
INSERT INTO [Categories]
([CategoryName], [Description], [BrochurePath], [Picture])
VALUES
(@CategoryName, @Description, @BrochurePath, @Picture)
L’écran final de l’Assistant nous invite à nommer la nouvelle méthode TableAdapter. Entrez InsertWithPicture et cliquez sur Terminer.
Figure 2 : Nommer la méthode New TableAdapter InsertWithPicture (Cliquez pour voir l'image en taille réelle)
Étape 2 : Mise à jour de la couche logique métier
Étant donné que la couche Présentation ne doit s’interfacer qu’avec la couche logique métier plutôt que de la contourner pour accéder directement à la couche d’accès aux données, nous devons créer une méthode BLL qui appelle la méthode DAL que nous venons de créer (InsertWithPicture). Pour ce tutoriel, créez une méthode dans la CategoriesBLL classe nommée InsertWithPicture qui accepte comme entrée trois string s et un byte tableau. Les paramètres d'entrée string sont destinés au nom de la catégorie, à sa description et au chemin du fichier de brochure, tandis que le tableau byte est destiné aux contenus binaires de l'image de la catégorie. Comme le montre le code suivant, cette méthode BLL appelle la méthode DAL correspondante :
[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);
}
Remarque
Vérifiez que vous avez enregistré l’ensemble de données typé avant d’ajouter la méthode InsertWithPicture à la BLL. Étant donné que le CategoriesTableAdapter code de classe est généré automatiquement en fonction du jeu de données typé, si vous ne sauvegardez pas d'abord vos modifications dans le jeu de données typé, la Adapter propriété ne connaîtra pas la InsertWithPicture méthode.
Étape 3 : Répertorier les catégories existantes et leurs données binaires
Dans ce tutoriel, nous allons créer une page qui permet à un utilisateur final d’ajouter une nouvelle catégorie au système, en fournissant une image et une brochure pour la nouvelle catégorie. Dans le tutoriel précédent , nous avons utilisé un GridView avec un TemplateField et ImageField pour afficher le nom, la description, l’image de chaque catégorie et un lien pour télécharger sa brochure. Répliquer cette fonctionnalité pour ce didacticiel, en créant une page qui répertorie toutes les catégories existantes et en permet la création.
Commencez par ouvrir la DisplayOrDownload.aspx page à partir du BinaryData dossier. Accédez à la vue Source et copiez la syntaxe déclarative de GridView et ObjectDataSource, en la collant dans l’élément<asp:Content>.UploadInDetailsView.aspx En outre, n’oubliez pas de copier la méthode de la classe de code-behind de GenerateBrochureLink dans DisplayOrDownload.aspx.
Figure 3 : Copier et coller la syntaxe déclarative à partir de DisplayOrDownload.aspx (UploadInDetailsView.aspxCliquez pour afficher l’image de taille complète)
Après avoir copié la syntaxe déclarative et GenerateBrochureLink la méthode sur la UploadInDetailsView.aspx page, affichez la page via un navigateur pour vous assurer que tout a été copié correctement. Vous devriez voir un GridView répertoriant les huit catégories qui incluent un lien pour télécharger la brochure ainsi que l’image de la catégorie.
Figure 4 : Vous devez maintenant voir chaque catégorie avec ses données binaires (cliquez pour afficher l’image de taille complète)
Étape 4 : Configurer le CategoriesDataSource pour supporter l'insertion
L’ObjectDataSource CategoriesDataSource utilisé par Categories GridView ne fournit actuellement pas la possibilité d’insérer des données. Pour prendre en charge l'insertion via ce contrôle de source de données, nous devons associer sa méthode Insert à une méthode dans son objet sous-jacent, CategoriesBLL. En particulier, nous voulons le mapper à la méthode que nous avons ajoutée à l'étape 2 CategoriesBLL, InsertWithPicture.
Commencez par cliquer sur le lien Configurer la source de données à partir de la balise active ObjectDataSource. Le premier écran montre l’objet avec lequel la source de données est configurée pour fonctionner. CategoriesBLL Laissez ce paramètre as-is, puis cliquez sur Suivant pour accéder à l’écran Définir les méthodes de données. Accédez à l’onglet INSERT et sélectionnez la InsertWithPicture méthode dans la liste déroulante. Cliquez sur Terminer pour finaliser l'Assistant.
Figure 5 : Configurer ObjectDataSource pour utiliser la méthode (InsertWithPicture de taille complète)
Remarque
Une fois l’Assistant terminé, Visual Studio peut vous demander si vous souhaitez actualiser les champs et les clés, afin de régénérer les champs des contrôles Web de données. Choisissez Non, car le choix oui remplacera les personnalisations de champ que vous avez peut-être effectuées.
Une fois l’Assistant terminé, l’ObjectDataSource inclut désormais une valeur pour sa propriété InsertMethod, ainsi qu'une valeur pour les quatre colonnes de catégorie InsertParameters, comme le montre le balisage déclaratif suivant :
<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>
Étape 5 : Création de l’interface d’insertion
Comme indiqué dans la section Vue d’ensemble de l’insertion, de la mise à jour et de la suppression de données, le contrôle DetailsView fournit une interface d’insertion intégrée qui peut être utilisée lors de l’utilisation d’un contrôle de source de données qui prend en charge l’insertion. Ajoutons un contrôle DetailsView à cette page au-dessus de GridView qui affiche définitivement son interface d’insertion, ce qui permet à un utilisateur d’ajouter rapidement une nouvelle catégorie. Lors de l’ajout d’une nouvelle catégorie dans DetailsView, gridView s’actualise automatiquement et affiche la nouvelle catégorie.
Commencez par faire glisser un DetailsView depuis la boîte à outils vers le Concepteur au-dessus du GridView, en définissant sa propriété ID sur NewCategory et en effaçant les valeurs des propriétés Height et Width. À partir de la balise active DetailsView, liez-la à l’existant CategoriesDataSource , puis cochez la case Activer l’insertion.
Figure 6: Lier le DetailsView à CategoriesDataSource et activer l'insertion (cliquez pour afficher l'image de taille complète)
Pour afficher définitivement DetailsView dans son interface d'insertion, définissez sa propriété DefaultMode à Insert.
Notez que DetailsView a cinq BoundFields CategoryID, CategoryName, Description, NumberOfProducts et BrochurePath, bien que le BoundField CategoryID ne soit pas rendu dans l’interface d’insertion, car sa propriété InsertVisible est définie sur false. Ces BoundFields existent, car ils sont les colonnes retournées par la GetCategories() méthode, c’est-à-dire ce que l’ObjectDataSource appelle pour récupérer ses données. Toutefois, pour l’insertion, nous ne voulons pas permettre à l’utilisateur de spécifier une valeur pour NumberOfProducts. En outre, nous devons leur permettre de charger une image pour la nouvelle catégorie, ainsi que de charger un PDF pour la brochure.
Supprimez complètement le BoundField NumberOfProducts du DetailsView, puis mettez à jour les BoundFields HeaderText et CategoryName aux catégories "Catégorie" et "Brochure", respectivement. Ensuite, convertissez le BoundField BrochurePath en TemplateField et ajoutez un nouveau TemplateField pour l'image, en donnant à ce nouveau TemplateField une HeaderText valeur de Picture. Déplacez le Picture TemplateField pour qu’il se trouve entre BrochurePath TemplateField et CommandField.
Figure 7 : Lier le DetailsView à CategoriesDataSource et activer l’insertion
Si vous avez converti BoundField BrochurePath en un TemplateField via la boîte de dialogue Modifier les champs, templateField inclut un ItemTemplate, EditItemTemplateet InsertItemTemplate. Seul le InsertItemTemplate est nécessaire. Toutefois, vous pouvez librement supprimer les deux autres modèles. À ce stade, la syntaxe déclarative de DetailsView doit ressembler à ce qui suit :
<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>
Ajout de contrôles FileUpload pour les champs Brochure et Image
Actuellement, l’objet BrochurePath TemplateField InsertItemTemplate contient une zone de texte, tandis que TemplateField Picture ne contient aucun modèle. Nous devons mettre à jour ces deux TemplateFields afin d'utiliser les contrôles FileUpload.
Dans la balise active DetailsView, choisissez l’option Modifier les modèles, puis sélectionnez templateField BrochurePath s InsertItemTemplate dans la liste déroulante. Supprimez la zone de texte, puis faites glisser un contrôle FileUpload de la boîte à outils dans le modèle. Définissez le contrôle FileUpload sur IDBrochureUpload. De même, ajoutez un contrôle FileUpload à templateField Picture s InsertItemTemplate. Définissez ce contrôle de téléchargement de fichiers sur IDPictureUpload.
Figure 8 : Ajouter un contrôle FileUpload à l’objet InsertItemTemplate (Cliquez pour afficher l’image de taille complète)
Après avoir effectué ces ajouts, la syntaxe déclarative de deux TemplateField sera :
<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>
Lorsqu’un utilisateur ajoute une nouvelle catégorie, nous voulons nous assurer que la brochure et l’image sont du type de fichier correct. Pour la brochure, l’utilisateur doit fournir un fichier PDF. Pour l’image, nous avons besoin de l’utilisateur pour charger un fichier image, mais autorise-t-il un fichier image ou uniquement des fichiers image d’un type particulier, tels que les fichiers GIF ou JPG ? Pour autoriser différents types de fichiers, nous devons étendre le Categories schéma pour inclure une colonne qui capture le type de fichier afin que ce type puisse être envoyé au client via Response.ContentTypeDisplayCategoryPicture.aspx. Étant donné que nous n’avons pas de telle colonne, il serait prudent de restreindre les utilisateurs à fournir uniquement un type de fichier image spécifique. Les Categories images existantes du tableau sont des bitmaps, mais les JPG sont un format de fichier plus approprié pour les images servies sur le web.
Si un utilisateur charge un type de fichier incorrect, nous devons annuler l’insertion et afficher un message indiquant le problème. Ajoutez un contrôle Web Label sous DetailsView. Définissez la propriété ID sur UploadWarning, effacez la propriété Text, définissez la propriété CssClass sur Avertissement et les propriétés Visible et EnableViewState sur false. La Warning classe CSS est définie dans Styles.css et restitue le texte dans une police rouge, italique et en gras.
Remarque
Dans l'idéal, les BoundFields CategoryName et Description seraient convertis en TemplateFields et leurs interfaces d'insertion personnalisées. L’interface Description d’insertion, par exemple, serait probablement mieux adaptée par le biais d’une zone de texte multiligne. Et étant donné que la colonne CategoryName n’accepte pas les valeurs NULL, un RequiredFieldValidator doit être ajouté pour garantir que l’utilisateur fournisse une valeur pour le nom de la nouvelle catégorie. Ces étapes sont laissées en tant qu’exercice au lecteur. Reportez-vous à la personnalisation de l’interface de modification des données pour un examen approfondi de l’augmentation des interfaces de modification des données.
Étape 6 : Enregistrement de la brochure chargée dans le système de fichiers du serveur web
Lorsque l’utilisateur entre les valeurs d’une nouvelle catégorie et clique sur le bouton Insérer, un postback se produit et le flux de travail d’insertion se déroule. Tout d’abord, l’événement Insert() est appelée, ce qui entraîne l’ajout d’un nouvel enregistrement à la Categories table. Après cela, l’événementItemInserted DetailsView se déclenche.
Avant l’appel de la méthode ObjectDataSource, nous devons d’abord vérifier que les types de Insert() fichiers appropriés ont été chargés par l’utilisateur, puis enregistrez le fichier PDF de la brochure dans le système de fichiers du serveur web. Créez un gestionnaire d’événements pour l’événement DetailsView ItemInserting et ajoutez le code suivant :
// 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;
}
}
Le gestionnaire d’événements commence par référencer l'élément de contrôle BrochureUpload FileUpload à partir des modèles de DetailsView. Ensuite, si une brochure a été chargée, l’extension du fichier chargé est examinée. Si l’extension n’est pas .PDF, un avertissement s’affiche, l’insertion est annulée et l’exécution du gestionnaire d’événements se termine.
Remarque
Le fait de compter sur l’extension du fichier chargé n’est pas une technique sure-fire pour s’assurer que le fichier chargé est un document PDF. L’utilisateur peut avoir un document PDF valide avec l’extension .Brochure, ou avoir pris un document non-PDF et lui avoir donné une .pdf extension. Le contenu binaire du fichier doit être examiné par programmation afin de vérifier de manière plus concluante le type de fichier. Ces approches approfondies, cependant, sont souvent excessives ; la vérification de l’extension est suffisante pour la plupart des scénarios.
Comme indiqué dans le didacticiel Chargement de fichiers, il est important de prendre soin lors de l'enregistrement des fichiers dans le système de fichiers afin qu’un téléchargement par un utilisateur ne remplace pas celui d’un autre. Pour ce tutoriel, nous tenterons d’utiliser le même nom que le fichier chargé. S’il existe déjà un fichier dans le ~/Brochures répertoire portant ce même nom de fichier, toutefois, nous ajouterons un nombre à la fin jusqu’à ce qu’un nom unique soit trouvé. Par exemple, si l'utilisateur télécharge un fichier de brochure nommé Meats.pdf, mais qu'un fichier portant le nom Meats.pdf existe déjà dans le dossier ~/Brochures, nous allons modifier le nom du fichier enregistré en Meats-1.pdf. S’il en existe, nous allons essayer Meats-2.pdf, et ainsi de suite, jusqu’à ce qu’un nom de fichier unique soit trouvé.
Le code suivant utilise la File.Exists(path) méthode pour déterminer si un fichier existe déjà avec le nom de fichier spécifié. Dans ce cas, il continue d’essayer de nouveaux noms de fichiers pour la brochure jusqu’à ce qu’aucun conflit ne soit trouvé.
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++;
}
Une fois qu’un nom de fichier valide a été trouvé, le fichier doit être enregistré dans le système de fichiers et la valeur ObjectDataSource brochurePath``InsertParameter doit être mise à jour afin que ce nom de fichier soit écrit dans la base de données. Comme nous l’avons vu dans le didacticiel intitulé Uploading Files, le fichier peut être enregistré à l’aide d’une méthode du contrôle SaveAs(path) FileUpload. Pour mettre à jour le paramètre ObjectDataSource brochurePath , utilisez la e.Values collection.
// Save the file to disk and set the value of the brochurePath parameter
BrochureUpload.SaveAs(Server.MapPath(brochurePath));
e.Values["brochurePath"] = brochurePath;
Étape 7 : Enregistrement de l’image chargée dans la base de données
Pour stocker l’image chargée dans le nouvel Categories enregistrement, nous devons affecter le contenu binaire chargé au paramètre ObjectDataSource s picture dans l’événement DetailsView.ItemInserting Avant d’effectuer cette affectation, toutefois, nous devons d’abord vérifier que l’image chargée est un JPG et non un autre type d’image. Comme à l’étape 6, nous allons utiliser l’extension de fichier de l’image chargée pour déterminer son type.
Bien que la Categories table autorise les valeurs NULL pour la colonne Picture, actuellement, toutes les catégories ont une image. Forcez l’utilisateur à fournir une image lors de l’ajout d’une nouvelle catégorie via cette page. Le code suivant vérifie qu’une image a été chargée et qu’elle a une extension approprié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;
}
Ce code doit être placé avant le code de l’étape 6 afin qu’en cas de problème avec le chargement de l’image, le gestionnaire d’événements se termine avant l’enregistrement du fichier de brochure dans le système de fichiers.
En supposant qu’un fichier approprié a été chargé, affectez le contenu binaire chargé à la valeur du paramètre image avec la ligne de code suivante :
// Set the value of the picture parameter
e.Values["picture"] = PictureUpload.FileBytes;
Le Gestionnaire d’événements completItemInserting
Pour l’exhaustivité, voici le ItemInserting gestionnaire d’événements dans son intégralité :
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;
}
}
Étape 8 : Correction de laDisplayCategoryPicture.aspxpage
Prenons un moment pour tester l’interface d’insertion et ItemInserting le gestionnaire d’événements créés au cours des dernières étapes. Visitez la UploadInDetailsView.aspx page via un navigateur et tentez d’ajouter une catégorie, mais omettez l’image, ou spécifiez une image non-JPG ou une brochure non-PDF. Dans l’un de ces cas, un message d’erreur s’affiche et le flux de travail d’insertion est annulé.
Figure 9 : Un message d’avertissement s’affiche si un type de fichier non valide est chargé (cliquez pour afficher l’image de taille complète)
Une fois que vous avez vérifié que la page exige qu’une image soit chargée et qu’elle n’accepte pas les fichiers non PDF ou non JPG, ajoutez une nouvelle catégorie avec une image JPG valide, laissant le champ Brochure vide. Après avoir cliqué sur le bouton Insérer, la page se rechargera et un nouvel enregistrement sera ajouté à la table Categories avec le contenu binaire de l'image téléchargée stocké directement dans la base de données. GridView est mis à jour et affiche une ligne pour la catégorie nouvellement ajoutée, mais, comme l’illustre la figure 10, la nouvelle image de catégorie n’est pas rendue correctement.
Figure 10 : L’image de la nouvelle catégorie n’est pas affichée (cliquez pour afficher l’image de taille complète)
La raison pour laquelle la nouvelle image n’est pas affichée est parce que la DisplayCategoryPicture.aspx page qui retourne une image de catégorie spécifiée est configurée pour traiter les bitmaps qui ont un en-tête OLE. Cet en-tête de 78 octets est supprimé du contenu binaire de la Picture colonne avant qu’il ne soit renvoyé au client. Mais le fichier JPG que nous venons de charger pour la nouvelle catégorie n’a pas cet en-tête OLE ; par conséquent, les octets valides et nécessaires sont supprimés des données binaires de l’image.
Étant donné qu’il existe maintenant des bitmaps avec en-têtes OLE et des JPG dans le tableau Categories, nous devons mettre à jour DisplayCategoryPicture.aspx pour qu'il réalise la suppression d’en-tête OLE pour les huit catégories d’origine et ignore cette suppression pour les enregistrements de catégorie plus récents. Dans notre prochain tutoriel, nous allons examiner comment mettre à jour une image d’enregistrement existante, et nous allons mettre à jour toutes les anciennes images de catégorie afin qu’elles soient des JPG. Pour l'instant, cependant, utilisez le code suivant dans DisplayCategoryPicture.aspx pour supprimer les en-têtes OLE uniquement pour ces huit catégories d'origine :
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);
}
}
Avec cette modification, l’image JPG est désormais rendue correctement dans GridView.
Figure 11 : Les images JPG pour les nouvelles catégories sont correctement affichées (cliquez pour afficher l’image de taille complète)
Étape 9 : Suppression de la brochure en cas d'exception
L’un des défis liés au stockage des données binaires sur le système de fichiers du serveur web est qu’il introduit une déconnexion entre le modèle de données et ses données binaires. Par conséquent, chaque fois qu’un enregistrement est supprimé, les données binaires correspondantes sur le système de fichiers doivent également être supprimées. Cela peut également entrer en jeu lors de l’insertion. Considérez le scénario suivant : un utilisateur ajoute une nouvelle catégorie, en spécifiant une image et une brochure valides. Lorsque vous cliquez sur le bouton Insérer, une publication se produit et l’événement DetailsView ItemInserting se déclenche, en enregistrant la brochure dans le système de fichiers du serveur web. Ensuite, la méthode Insert() de l'ObjectDataSource est appelée, qui appelle la méthode CategoriesBLL de la classe InsertWithPicture, qui appelle la méthode CategoriesTableAdapter de InsertWithPicture.
À présent, que se passe-t-il si la base de données est hors connexion ou s’il existe une erreur dans l’instruction INSERT SQL ? Clairement, l’insertion échoue, donc aucune nouvelle ligne de catégorie n’est ajoutée à la base de données. Mais nous avons toujours le fichier de la brochure téléchargé déposé sur le système de fichiers du serveur web. Ce fichier doit être supprimé face à une exception pendant l’insertion du flux de travail.
Comme indiqué précédemment dans le didacticiel Gestion des exceptions BLL et DAL-Level dans une page ASP.NET, lorsqu'une exception est levée à partir des profondeurs de l'architecture, elle remonte à travers les différentes couches. Dans la couche présentation, nous pouvons déterminer si une exception s’est produite à partir de l’événement DetailsView ItemInserted . Ce gestionnaire d’événements fournit également les valeurs de ObjectDataSource s InsertParameters. Par conséquent, nous pouvons créer un gestionnaire d’événements pour l’événement ItemInserted qui vérifie s’il y avait une exception et, le cas échéant, supprime le fichier spécifié par le paramètre ObjectDataSource brochurePath :
protected void NewCategory_ItemInserted
(object sender, DetailsViewInsertedEventArgs e)
{
if (e.Exception != null)
{
// Need to delete brochure file, if it exists
if (e.Values["brochurePath"] != null)
System.IO.File.Delete(Server.MapPath(
e.Values["brochurePath"].ToString()));
}
}
Résumé
Plusieurs étapes doivent être effectuées pour fournir une interface web permettant d’ajouter des enregistrements incluant des données binaires. Si les données binaires sont stockées directement dans la base de données, vous devez mettre à jour l’architecture, en ajoutant des méthodes spécifiques pour gérer le cas où les données binaires sont insérées. Une fois l’architecture mise à jour, l’étape suivante consiste à créer l’interface d’insertion, qui peut être effectuée à l’aide d’un DetailsView personnalisé pour inclure un contrôle FileUpload pour chaque champ de données binaires. Les données chargées peuvent ensuite être enregistrées dans le système de fichiers du serveur web ou affectées à un paramètre de source de données dans le gestionnaire d’événements DetailsView ItemInserting .
L’enregistrement de données binaires dans le système de fichiers nécessite plus de planification que d’enregistrer des données directement dans la base de données. Un schéma de nommage doit être choisi afin d'éviter que le téléchargement d'un utilisateur n'écrase celui d'un autre. En outre, des étapes supplémentaires doivent être effectuées pour supprimer le fichier chargé en cas d’échec de l’insertion de la base de données.
Nous avons maintenant la possibilité d’ajouter de nouvelles catégories au système avec une brochure et une image, mais nous n’avons pas encore examiné comment mettre à jour des données binaires de catégorie existantes ou comment supprimer correctement les données binaires d’une catégorie supprimée. Nous allons explorer ces deux rubriques dans le tutoriel suivant.
Bonne programmation !
À propos de l’auteur
Scott Mitchell, auteur de sept livres ASP/ASP.NET et fondateur de 4GuysFromRolla.com, travaille avec les technologies Web Microsoft depuis 1998. Scott travaille en tant que consultant indépendant, formateur et écrivain. Son dernier livre est Sams Teach Yourself ASP.NET 2.0 en 24 heures. On peut le joindre à mitchell@4GuysFromRolla.com.
Merci spécial à
Cette série de tutoriels a été examinée par de nombreux réviseurs utiles. Les réviseurs principaux de ce tutoriel étaient Dave Gardner, Teresa Murphy et Bernadette Leigh. Vous souhaitez consulter mes prochains articles MSDN ? Si c’est le cas, déposez-moi une ligne à mitchell@4GuysFromRolla.com.